| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright (c) 1988 AT&T |
| * Copyright (c) 1989 AT&T |
| * All Rights Reserved |
| * |
| * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Copyright 2018 Jason King |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <ctype.h> |
| #include <locale.h> |
| #include <libelf.h> |
| #include <sys/elf_SPARC.h> |
| |
| |
| /* exit return codes */ |
| #define NOARGS 1 |
| #define BADELF 2 |
| #define NOALLOC 3 |
| |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <dlfcn.h> |
| |
| #include "sgs.h" |
| #include "conv.h" |
| #include "gelf.h" |
| |
| typedef struct { /* structure to translate symbol table data */ |
| int indx; |
| char *name; |
| GElf_Addr value; |
| GElf_Xword size; |
| int type; |
| int bind; |
| unsigned char other; |
| unsigned int shndx; |
| unsigned int flags; /* flags relevant to entry */ |
| } SYM; |
| |
| #define FLG_SYM_SPECSEC 0x00000001 /* reserved scn index */ |
| /* (SHN_ABS, SHN_COMMON, ...) */ |
| |
| #define UNDEFINED "U" |
| #define BSS_GLOB "B" |
| #define BSS_WEAK "B*" |
| #define BSS_LOCL "b" |
| #define BSS_SECN ".bss" |
| #define REG_GLOB "R" |
| #define REG_WEAK "R*" |
| #define REG_LOCL "r" |
| |
| #define OPTSTR ":APDoxhvnursplLCVefgRTt:" /* option string for getopt() */ |
| |
| #define DATESIZE 60 |
| |
| #define TYPE 7 |
| #define BIND 3 |
| |
| #define DEF_MAX_SYM_SIZE 256 |
| |
| static char *key[TYPE][BIND]; |
| |
| /* |
| * Format type used for printing value and size items. |
| * The non-negative values here are used as array indices into |
| * several arrays found below. Renumbering, or adding items, |
| * will require changes to those arrays as well. |
| */ |
| typedef enum { |
| FMT_T_NONE = -1, /* No format type yet assigned */ |
| |
| /* The following are used as array indices */ |
| FMT_T_DEC = 0, |
| FMT_T_HEX = 1, |
| FMT_T_OCT = 2 |
| } FMT_T; |
| |
| /* |
| * Determine whether a proposed format type is compatible with the current |
| * setting. We allow setting the format as long as it hasn't already |
| * been done, or if the new setting is the same as the current one. |
| */ |
| #define COMPAT_FMT_FLAG(new_fmt_flag) \ |
| (fmt_flag == FMT_T_NONE) || (fmt_flag == new_fmt_flag) |
| |
| static FMT_T fmt_flag = FMT_T_NONE; /* format style to use for value/size */ |
| |
| static int /* flags: ?_flag corresponds to ? option */ |
| h_flag = 0, /* suppress printing of headings */ |
| v_flag = 0, /* sort external symbols by value */ |
| n_flag = 0, /* sort external symbols by name */ |
| u_flag = 0, /* print only undefined symbols */ |
| r_flag = 0, /* prepend object file or archive name */ |
| /* to each symbol name */ |
| R_flag = 0, /* if "-R" issued then prepend archive name, */ |
| /* object file name to each symbol */ |
| s_flag = 0, /* print section name instead of section index */ |
| p_flag = 0, /* produce terse output */ |
| P_flag = 0, /* Portable format output */ |
| l_flag = 0, /* produce long listing of output */ |
| L_flag = 0, /* print SUNW_LDYNSYM instead of SYMTAB */ |
| D_flag = 0, /* print DYNSYM instead of SYMTAB */ |
| C_flag = 0, /* print decoded C++ names */ |
| A_flag = 0, /* File name */ |
| e_flag = 0, /* -e flag */ |
| g_flag = 0, /* -g flag */ |
| V_flag = 0; /* print version information */ |
| static char A_header[DEF_MAX_SYM_SIZE+1] = {0}; |
| |
| static char *prog_name; |
| static char *archive_name = (char *)0; |
| static int errflag = 0; |
| static void usage(); |
| static void each_file(char *); |
| static void process(Elf *, char *); |
| static Elf_Scn * get_scnfd(Elf *, int, int); |
| static void get_symtab(Elf *, char *); |
| static SYM * readsyms(Elf_Data *, GElf_Sxword, Elf *, unsigned int, |
| unsigned int); |
| static int compare(SYM *, SYM *); |
| static char *lookup(int, int); |
| static int is_bss_section(unsigned int, Elf *, unsigned int); |
| static void print_ar_files(int, Elf *, char *); |
| static void print_symtab(Elf *, unsigned int, Elf_Scn *, GElf_Shdr *, char *); |
| static void parsename(char *); |
| static void parse_fn_and_print(const char *, char *); |
| static char d_buf[512]; |
| static char p_buf[512]; |
| static int exotic(const char *s); |
| static void set_A_header(char *); |
| static char *FormatName(char *, const char *); |
| |
| |
| |
| /* |
| * Parses the command line options and then |
| * calls each_file() to process each file. |
| */ |
| int |
| main(int argc, char *argv[], char *envp[]) |
| { |
| char *optstr = OPTSTR; /* option string used by getopt() */ |
| int optchar; |
| FMT_T new_fmt_flag; |
| |
| #ifndef XPG4 |
| /* |
| * Check for a binary that better fits this architecture. |
| */ |
| (void) conv_check_native(argv, envp); |
| #endif |
| |
| /* table of keyletters for use with -p and -P options */ |
| key[STT_NOTYPE][STB_LOCAL] = "n"; |
| key[STT_NOTYPE][STB_GLOBAL] = "N"; |
| key[STT_NOTYPE][STB_WEAK] = "N*"; |
| key[STT_OBJECT][STB_LOCAL] = "d"; |
| key[STT_OBJECT][STB_GLOBAL] = "D"; |
| key[STT_OBJECT][STB_WEAK] = "D*"; |
| key[STT_FUNC][STB_LOCAL] = "t"; |
| key[STT_FUNC][STB_GLOBAL] = "T"; |
| key[STT_FUNC][STB_WEAK] = "T*"; |
| key[STT_SECTION][STB_LOCAL] = "s"; |
| key[STT_SECTION][STB_GLOBAL] = "S"; |
| key[STT_SECTION][STB_WEAK] = "S*"; |
| key[STT_FILE][STB_LOCAL] = "f"; |
| key[STT_FILE][STB_GLOBAL] = "F"; |
| key[STT_FILE][STB_WEAK] = "F*"; |
| key[STT_COMMON][STB_LOCAL] = "c"; |
| key[STT_COMMON][STB_GLOBAL] = "C"; |
| key[STT_COMMON][STB_WEAK] = "C*"; |
| key[STT_TLS][STB_LOCAL] = "l"; |
| key[STT_TLS][STB_GLOBAL] = "L"; |
| key[STT_TLS][STB_WEAK] = "L*"; |
| |
| prog_name = argv[0]; |
| |
| (void) setlocale(LC_ALL, ""); |
| #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ |
| #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ |
| #endif |
| (void) textdomain(TEXT_DOMAIN); |
| |
| while ((optchar = getopt(argc, argv, optstr)) != -1) { |
| switch (optchar) { |
| case 'o': if (COMPAT_FMT_FLAG(FMT_T_OCT)) |
| fmt_flag = FMT_T_OCT; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -x or -t set, -o ignored\n"), |
| prog_name); |
| break; |
| case 'x': if (COMPAT_FMT_FLAG(FMT_T_HEX)) |
| fmt_flag = FMT_T_HEX; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -o or -t set, -x ignored\n"), |
| prog_name); |
| break; |
| case 'h': h_flag = 1; |
| break; |
| case 'v': if (!n_flag) |
| v_flag = 1; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -n set, -v ignored\n"), |
| prog_name); |
| break; |
| case 'n': if (!v_flag) |
| n_flag = 1; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -v set, -n ignored\n"), |
| prog_name); |
| break; |
| case 'u': if (!e_flag && !g_flag) |
| u_flag = 1; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -e or -g set, -u ignored\n"), |
| prog_name); |
| break; |
| case 'e': if (!u_flag && !g_flag) |
| e_flag = 1; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -u or -g set, -e ignored\n"), |
| prog_name); |
| break; |
| case 'g': if (!u_flag && !e_flag) |
| g_flag = 1; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -u or -e set, -g ignored\n"), |
| prog_name); |
| break; |
| case 'r': if (R_flag) { |
| R_flag = 0; |
| (void) fprintf(stderr, gettext( |
| "%s: -r set, -R ignored\n"), |
| prog_name); |
| } |
| r_flag = 1; |
| break; |
| case 's': s_flag = 1; |
| break; |
| case 'p': if (P_flag == 1) { |
| (void) fprintf(stderr, gettext( |
| "nm: -P set. -p ignored\n")); |
| } else |
| p_flag = 1; |
| break; |
| case 'P': if (p_flag == 1) { |
| (void) fprintf(stderr, gettext( |
| "nm: -p set. -P ignored\n")); |
| } else |
| P_flag = 1; |
| break; |
| case 'l': l_flag = 1; |
| break; |
| case 'L': if (D_flag == 1) { |
| (void) fprintf(stderr, gettext( |
| "nm: -D set. -L ignored\n")); |
| } else |
| L_flag = 1; |
| break; |
| case 'D': if (L_flag == 1) { |
| (void) fprintf(stderr, gettext( |
| "nm: -L set. -D ignored\n")); |
| } else |
| D_flag = 1; |
| break; |
| case 'C': |
| C_flag = 1; |
| break; |
| case 'A': A_flag = 1; |
| break; |
| case 'V': V_flag = 1; |
| (void) fprintf(stderr, "nm: %s %s\n", |
| (const char *)SGU_PKG, |
| (const char *)SGU_REL); |
| break; |
| case 'f': /* -f is a noop, see man page */ |
| break; |
| case 'R': if (!r_flag) |
| R_flag = 1; |
| else |
| (void) fprintf(stderr, gettext( |
| "%s: -r set, -R ignored\n"), |
| prog_name); |
| break; |
| case 'T': |
| break; |
| case 't': if (strcmp(optarg, "o") == 0) { |
| new_fmt_flag = FMT_T_OCT; |
| } else if (strcmp(optarg, "d") == 0) { |
| new_fmt_flag = FMT_T_DEC; |
| } else if (strcmp(optarg, "x") == 0) { |
| new_fmt_flag = FMT_T_HEX; |
| } else { |
| new_fmt_flag = FMT_T_NONE; |
| } |
| if (new_fmt_flag == FMT_T_NONE) { |
| errflag += 1; |
| (void) fprintf(stderr, gettext( |
| "nm: -t requires radix value (d, o, x): %s\n"), optarg); |
| } else if (COMPAT_FMT_FLAG(new_fmt_flag)) { |
| fmt_flag = new_fmt_flag; |
| } else { |
| (void) fprintf(stderr, gettext( |
| "nm: -t or -o or -x set. -t ignored.\n")); |
| } |
| break; |
| case ':': errflag += 1; |
| (void) fprintf(stderr, gettext( |
| "nm: %c requires operand\n"), optopt); |
| break; |
| case '?': errflag += 1; |
| break; |
| default: break; |
| } |
| } |
| |
| if (errflag || (optind >= argc)) { |
| if (!(V_flag && (argc == 2))) { |
| usage(); |
| exit(NOARGS); |
| } |
| } |
| |
| /* |
| * If no explicit format style was specified, set the default |
| * here. In general, the default is for value and size items |
| * to be displayed in decimal format. The exception is that |
| * the default for -P is hexidecimal. |
| */ |
| if (fmt_flag == FMT_T_NONE) |
| fmt_flag = P_flag ? FMT_T_HEX : FMT_T_DEC; |
| |
| |
| while (optind < argc) { |
| each_file(argv[optind]); |
| optind++; |
| } |
| return (errflag); |
| } |
| |
| /* |
| * Print out a usage message in short form when program is invoked |
| * with insufficient or no arguments, and in long form when given |
| * either a ? or an invalid option. |
| */ |
| static void |
| usage() |
| { |
| (void) fprintf(stderr, gettext( |
| "Usage: nm [-ACDhLlnPpRrsTVv] [-efox] [-g | -u] [-t d|o|x] file ...\n")); |
| } |
| |
| /* |
| * Takes a filename as input. Test first for a valid version |
| * of libelf.a and exit on error. Process each valid file |
| * or archive given as input on the command line. Check |
| * for file type. If it is an archive, call print_ar_files |
| * to process each member of the archive in the same manner |
| * as object files on the command line. The same tests for |
| * valid object file type apply to regular archive members. |
| * If it is an ELF object file, process it; otherwise |
| * warn that it is an invalid file type and return from |
| * processing the file. |
| */ |
| |
| static void |
| each_file(char *filename) |
| { |
| Elf *elf_file; |
| int fd; |
| Elf_Kind file_type; |
| |
| struct stat64 buf; |
| |
| Elf_Cmd cmd; |
| errno = 0; |
| if (stat64(filename, &buf) == -1) { |
| (void) fprintf(stderr, "%s: ", prog_name); |
| perror(filename); |
| errflag++; |
| return; |
| } |
| if (elf_version(EV_CURRENT) == EV_NONE) { |
| (void) fprintf(stderr, gettext( |
| "%s: %s: libelf is out of date\n"), |
| prog_name, filename); |
| exit(BADELF); |
| } |
| |
| if ((fd = open((filename), O_RDONLY)) == -1) { |
| (void) fprintf(stderr, gettext("%s: %s: cannot read file\n"), |
| prog_name, filename); |
| errflag++; |
| return; |
| } |
| cmd = ELF_C_READ; |
| if ((elf_file = elf_begin(fd, cmd, (Elf *) 0)) == NULL) { |
| (void) fprintf(stderr, |
| "%s: %s: %s\n", prog_name, filename, elf_errmsg(-1)); |
| errflag++; |
| (void) close(fd); |
| return; |
| } |
| file_type = elf_kind(elf_file); |
| if (file_type == ELF_K_AR) { |
| print_ar_files(fd, elf_file, filename); |
| } else { |
| if (file_type == ELF_K_ELF) { |
| #ifndef XPG4 |
| if (u_flag && !h_flag) { |
| /* |
| * u_flag is specified. |
| */ |
| if (p_flag) |
| (void) printf("\n\n%s:\n\n", filename); |
| else |
| (void) printf(gettext( |
| "\n\nUndefined symbols from %s:\n\n"), |
| filename); |
| } else if (!h_flag & !P_flag) |
| #else |
| if (!h_flag & !P_flag) |
| #endif |
| { |
| if (p_flag) |
| (void) printf("\n\n%s:\n", filename); |
| else { |
| if (A_flag != 0) |
| (void) printf("\n\n%s%s:\n", |
| A_header, filename); |
| else |
| (void) printf("\n\n%s:\n", |
| filename); |
| } |
| } |
| archive_name = (char *)0; |
| process(elf_file, filename); |
| } else { |
| (void) fprintf(stderr, gettext( |
| "%s: %s: invalid file type\n"), |
| prog_name, filename); |
| errflag++; |
| } |
| } |
| (void) elf_end(elf_file); |
| (void) close(fd); |
| } |
| |
| /* |
| * Get the ELF header and, if it exists, call get_symtab() |
| * to begin processing of the file; otherwise, return from |
| * processing the file with a warning. |
| */ |
| static void |
| process(Elf *elf_file, char *filename) |
| { |
| GElf_Ehdr ehdr; |
| |
| if (gelf_getehdr(elf_file, &ehdr) == NULL) { |
| (void) fprintf(stderr, |
| "%s: %s: %s\n", prog_name, filename, elf_errmsg(-1)); |
| return; |
| } |
| |
| set_A_header(filename); |
| get_symtab(elf_file, filename); |
| } |
| |
| /* |
| * Get section descriptor for the associated string table |
| * and verify that the type of the section pointed to is |
| * indeed of type STRTAB. Returns a valid section descriptor |
| * or NULL on error. |
| */ |
| static Elf_Scn * |
| get_scnfd(Elf * e_file, int shstrtab, int SCN_TYPE) |
| { |
| Elf_Scn *fd_scn; |
| GElf_Shdr shdr; |
| |
| if ((fd_scn = elf_getscn(e_file, shstrtab)) == NULL) { |
| return (NULL); |
| } |
| |
| (void) gelf_getshdr(fd_scn, &shdr); |
| if (shdr.sh_type != SCN_TYPE) { |
| return (NULL); |
| } |
| return (fd_scn); |
| } |
| |
| |
| /* |
| * Print the symbol table. This function does not print the contents |
| * of the symbol table but sets up the parameters and then calls |
| * print_symtab to print the symbols. This function does not assume |
| * that there is only one section of type SYMTAB. Input is an opened |
| * ELF file, a pointer to the ELF header, and the filename. |
| */ |
| static void |
| get_symtab(Elf *elf_file, char *filename) |
| { |
| Elf_Scn *scn, *scnfd; |
| Elf_Data *data; |
| GElf_Word symtabtype; |
| size_t shstrndx; |
| |
| if (elf_getshdrstrndx(elf_file, &shstrndx) == -1) { |
| (void) fprintf(stderr, gettext( |
| "%s: %s: cannot get e_shstrndx\n"), |
| prog_name, filename); |
| return; |
| } |
| |
| /* get section header string table */ |
| scnfd = get_scnfd(elf_file, shstrndx, SHT_STRTAB); |
| if (scnfd == NULL) { |
| (void) fprintf(stderr, gettext( |
| "%s: %s: cannot get string table\n"), |
| prog_name, filename); |
| return; |
| } |
| |
| data = elf_getdata(scnfd, NULL); |
| if (data->d_size == 0) { |
| (void) fprintf(stderr, gettext( |
| "%s: %s: no data in string table\n"), |
| prog_name, filename); |
| return; |
| } |
| |
| if (D_flag) |
| symtabtype = SHT_DYNSYM; |
| else if (L_flag) |
| symtabtype = SHT_SUNW_LDYNSYM; |
| else |
| symtabtype = SHT_SYMTAB; |
| |
| scn = 0; |
| while ((scn = elf_nextscn(elf_file, scn)) != 0) { |
| GElf_Shdr shdr; |
| |
| if (gelf_getshdr(scn, &shdr) == NULL) { |
| (void) fprintf(stderr, "%s: %s: %s:\n", |
| prog_name, filename, elf_errmsg(-1)); |
| return; |
| } |
| |
| if (shdr.sh_type == symtabtype) { |
| print_symtab(elf_file, shstrndx, scn, |
| &shdr, filename); |
| } |
| } /* end while */ |
| } |
| |
| /* |
| * Process member files of an archive. This function provides |
| * a loop through an archive equivalent the processing of |
| * each_file for individual object files. |
| */ |
| static void |
| print_ar_files(int fd, Elf * elf_file, char *filename) |
| { |
| Elf_Arhdr *p_ar; |
| Elf *arf; |
| Elf_Cmd cmd; |
| Elf_Kind file_type; |
| |
| |
| cmd = ELF_C_READ; |
| archive_name = filename; |
| while ((arf = elf_begin(fd, cmd, elf_file)) != 0) { |
| p_ar = elf_getarhdr(arf); |
| if (p_ar == NULL) { |
| (void) fprintf(stderr, "%s: %s: %s\n", |
| prog_name, filename, elf_errmsg(-1)); |
| return; |
| } |
| if (p_ar->ar_name[0] == '/') { |
| cmd = elf_next(arf); |
| (void) elf_end(arf); |
| continue; |
| } |
| |
| if (!h_flag & !P_flag) { |
| if (p_flag) |
| (void) printf("\n\n%s[%s]:\n", |
| filename, p_ar->ar_name); |
| else { |
| if (A_flag != 0) |
| (void) printf("\n\n%s%s[%s]:\n", |
| A_header, filename, p_ar->ar_name); |
| else |
| (void) printf("\n\n%s[%s]:\n", |
| filename, p_ar->ar_name); |
| } |
| } |
| file_type = elf_kind(arf); |
| if (file_type == ELF_K_ELF) { |
| process(arf, p_ar->ar_name); |
| } else { |
| (void) fprintf(stderr, gettext( |
| "%s: %s: invalid file type\n"), |
| prog_name, p_ar->ar_name); |
| cmd = elf_next(arf); |
| (void) elf_end(arf); |
| errflag++; |
| continue; |
| } |
| |
| cmd = elf_next(arf); |
| (void) elf_end(arf); |
| } /* end while */ |
| } |
| |
| static void print_header(int); |
| #ifndef XPG4 |
| static void print_with_uflag(SYM *, char *); |
| #endif |
| static void print_with_pflag(int, Elf *, unsigned int, SYM *, char *); |
| static void print_with_Pflag(int, Elf *, unsigned int, SYM *); |
| static void print_with_otherflags(int, Elf *, unsigned int, |
| SYM *, char *); |
| /* |
| * Print the symbol table according to the flags that were |
| * set, if any. Input is an opened ELF file, the section name, |
| * the section header, the section descriptor, and the filename. |
| * First get the symbol table with a call to elf_getdata. |
| * Then translate the symbol table data in memory by calling |
| * readsyms(). This avoids duplication of function calls |
| * and improves sorting efficiency. qsort is used when sorting |
| * is requested. |
| */ |
| static void |
| print_symtab(Elf *elf_file, unsigned int shstrndx, |
| Elf_Scn *p_sd, GElf_Shdr *shdr, char *filename) |
| { |
| |
| Elf_Data * sd; |
| SYM *sym_data; |
| SYM *s; |
| GElf_Sxword count = 0; |
| const int ndigits_arr[] = { |
| 10, /* FMT_T_DEC */ |
| 8, /* FMT_T_HEX */ |
| 11, /* FMT_T_OCT */ |
| }; |
| int ndigits; |
| |
| /* |
| * Determine # of digits to use for each numeric value. |
| */ |
| ndigits = ndigits_arr[fmt_flag]; |
| if (gelf_getclass(elf_file) == ELFCLASS64) |
| ndigits *= 2; |
| |
| /* |
| * print header |
| */ |
| print_header(ndigits); |
| |
| /* |
| * get symbol table data |
| */ |
| if (((sd = elf_getdata(p_sd, NULL)) == NULL) || (sd->d_size == 0)) { |
| (void) fprintf(stderr, |
| gettext("%s: %s: no symbol table data\n"), |
| prog_name, filename); |
| return; |
| } |
| count = shdr->sh_size / shdr->sh_entsize; |
| |
| /* |
| * translate symbol table data |
| */ |
| sym_data = readsyms(sd, count, elf_file, shdr->sh_link, |
| (unsigned int)elf_ndxscn(p_sd)); |
| if (sym_data == NULL) { |
| (void) fprintf(stderr, gettext( |
| "%s: %s: problem reading symbol data\n"), |
| prog_name, filename); |
| return; |
| } |
| qsort((char *)sym_data, count-1, sizeof (SYM), |
| (int (*)(const void *, const void *))compare); |
| s = sym_data; |
| while (count > 1) { |
| #ifndef XPG4 |
| if (u_flag) { |
| /* |
| * U_flag specified |
| */ |
| print_with_uflag(sym_data, filename); |
| } else if (p_flag) |
| #else |
| if (p_flag) |
| #endif |
| print_with_pflag(ndigits, elf_file, shstrndx, |
| sym_data, filename); |
| else if (P_flag) |
| print_with_Pflag(ndigits, elf_file, shstrndx, |
| sym_data); |
| else |
| print_with_otherflags(ndigits, elf_file, |
| shstrndx, sym_data, filename); |
| sym_data++; |
| count--; |
| } |
| |
| free(s); /* allocated in readsym() */ |
| } |
| |
| /* |
| * Return appropriate keyletter(s) for -p option. |
| * Returns an index into the key[][] table or NULL if |
| * the value of the keyletter is unknown. |
| */ |
| static char * |
| lookup(int a, int b) |
| { |
| return (((a < TYPE) && (b < BIND)) ? key[a][b] : NULL); |
| } |
| |
| /* |
| * Return TRUE(1) if the given section is ".bss" for "-p" option. |
| * Return FALSE(0) if not ".bss" section. |
| */ |
| static int |
| is_bss_section(unsigned int shndx, Elf * elf_file, unsigned int shstrndx) |
| { |
| Elf_Scn *scn = elf_getscn(elf_file, shndx); |
| char *sym_name; |
| |
| if (scn != NULL) { |
| GElf_Shdr shdr; |
| (void) gelf_getshdr(scn, &shdr); |
| sym_name = elf_strptr(elf_file, shstrndx, shdr.sh_name); |
| if (strcmp(BSS_SECN, sym_name) == 0) |
| return (1); |
| } |
| return (0); |
| } |
| |
| /* |
| * Translate symbol table data particularly for sorting. |
| * Input is the symbol table data structure, number of symbols, |
| * opened ELF file, and the string table link offset. |
| */ |
| static SYM * |
| readsyms(Elf_Data * data, GElf_Sxword num, Elf *elf, |
| unsigned int link, unsigned int symscnndx) |
| { |
| SYM *s, *buf; |
| GElf_Sym sym; |
| Elf32_Word *symshndx = 0; |
| unsigned int nosymshndx = 0; |
| int i; |
| |
| if ((buf = calloc(num, sizeof (SYM))) == NULL) { |
| (void) fprintf(stderr, gettext("%s: cannot allocate memory\n"), |
| prog_name); |
| return (NULL); |
| } |
| |
| s = buf; /* save pointer to head of array */ |
| |
| for (i = 1; i < num; i++, buf++) { |
| (void) gelf_getsym(data, i, &sym); |
| |
| buf->indx = i; |
| /* allow to work on machines where NULL-derefs dump core */ |
| if (sym.st_name == 0) |
| buf->name = ""; |
| else if (C_flag) { |
| const char *dn = NULL; |
| char *name = (char *)elf_strptr(elf, link, sym.st_name); |
| |
| dn = conv_demangle_name(name); |
| if (dn != name) { |
| name = FormatName(name, dn); |
| free((void *)dn); |
| } else if (exotic(name)) { |
| name = FormatName(name, d_buf); |
| } |
| buf->name = name; |
| } |
| else |
| buf->name = (char *)elf_strptr(elf, link, sym.st_name); |
| |
| buf->value = sym.st_value; |
| buf->size = sym.st_size; |
| buf->type = GELF_ST_TYPE(sym.st_info); |
| buf->bind = GELF_ST_BIND(sym.st_info); |
| buf->other = sym.st_other; |
| if ((sym.st_shndx == SHN_XINDEX) && |
| (symshndx == 0) && (nosymshndx == 0)) { |
| Elf_Scn *_scn; |
| GElf_Shdr _shdr; |
| _scn = 0; |
| while ((_scn = elf_nextscn(elf, _scn)) != 0) { |
| if (gelf_getshdr(_scn, &_shdr) == 0) |
| break; |
| if ((_shdr.sh_type == SHT_SYMTAB_SHNDX) && |
| (_shdr.sh_link == symscnndx)) { |
| Elf_Data *_data; |
| if ((_data = elf_getdata(_scn, |
| 0)) != 0) { |
| symshndx = |
| (Elf32_Word *)_data->d_buf; |
| break; |
| } |
| } |
| } |
| nosymshndx = 1; |
| } |
| if ((symshndx) && (sym.st_shndx == SHN_XINDEX)) { |
| buf->shndx = symshndx[i]; |
| } else { |
| buf->shndx = sym.st_shndx; |
| if (sym.st_shndx >= SHN_LORESERVE) |
| buf->flags |= FLG_SYM_SPECSEC; |
| } |
| } /* end for loop */ |
| return (s); |
| } |
| |
| /* |
| * compare either by name or by value for sorting. |
| * This is the comparison function called by qsort to |
| * sort the symbols either by name or value when requested. |
| */ |
| static int |
| compare(SYM *a, SYM *b) |
| { |
| if (v_flag) { |
| if (a->value > b->value) |
| return (1); |
| else |
| return ((a->value == b->value) -1); |
| } else |
| return ((int)strcoll(a->name, b->name)); |
| } |
| |
| /* |
| * Set up a header line for -A option. |
| */ |
| static void |
| set_A_header(char *fname) |
| { |
| if (A_flag == 0) |
| return; |
| |
| if (archive_name == (char *)0) { |
| (void) snprintf(A_header, sizeof (A_header), "%s: ", fname); |
| } else { |
| (void) snprintf(A_header, sizeof (A_header), "%s[%s]: ", |
| archive_name, fname); |
| } |
| } |
| |
| /* |
| * output functions |
| * The following functions are called from |
| * print_symtab(). |
| */ |
| |
| /* |
| * Print header line if needed. |
| * |
| * entry: |
| * ndigits - # of digits to be used to format an integer |
| * value, not counting any '0x' (hex) or '0' (octal) prefix. |
| */ |
| static void |
| print_header(int ndigits) |
| { |
| const char *fmt; |
| const char *section_title; |
| const int pad[] = { /* Extra prefix characters for format */ |
| 1, /* FMT_T_DEC: '|' */ |
| 3, /* FMT_T_HEX: '|0x' */ |
| 2, /* FMT_T_OCT: '|0' */ |
| }; |
| if ( |
| #ifndef XPG4 |
| !u_flag && |
| #endif |
| !h_flag && !p_flag && !P_flag) { |
| (void) printf("\n"); |
| if (!s_flag) { |
| fmt = "%-9s%-*s%-*s%-6s%-6s%-6s%-8s%s\n\n"; |
| section_title = "Shndx"; |
| } else { |
| fmt = "%-9s%-*s%-*s%-6s%-6s%-6s%-15s%s\n\n"; |
| section_title = "Shname"; |
| } |
| if (A_flag != 0) |
| (void) printf("%s", A_header); |
| ndigits += pad[fmt_flag]; |
| (void) printf(fmt, "[Index]", ndigits, " Value", |
| ndigits, " Size", "Type", "Bind", |
| "Other", section_title, "Name"); |
| } |
| } |
| |
| /* |
| * If the symbol can be printed, then return 1. |
| * If the symbol can not be printed, then return 0. |
| */ |
| static int |
| is_sym_print(SYM *sym_data) |
| { |
| /* |
| * If -u flag is specified, |
| * the symbol has to be undefined. |
| */ |
| if (u_flag != 0) { |
| if ((sym_data->shndx == SHN_UNDEF) && |
| (strlen(sym_data->name) != 0)) |
| return (1); |
| else |
| return (0); |
| } |
| |
| /* |
| * If -e flag is specified, |
| * the symbol has to be global or static. |
| */ |
| if (e_flag != 0) { |
| switch (sym_data->type) { |
| case STT_NOTYPE: |
| case STT_OBJECT: |
| case STT_FUNC: |
| case STT_COMMON: |
| case STT_TLS: |
| switch (sym_data->bind) { |
| case STB_LOCAL: |
| case STB_GLOBAL: |
| case STB_WEAK: |
| return (1); |
| default: |
| return (0); |
| } |
| default: |
| return (0); |
| } |
| } |
| |
| /* |
| * If -g is specified, |
| * the symbol has to be global. |
| */ |
| if (g_flag != 0) { |
| switch (sym_data->type) { |
| case STT_NOTYPE: |
| case STT_OBJECT: |
| case STT_FUNC: |
| case STT_COMMON: |
| case STT_TLS: |
| switch (sym_data->bind) { |
| case STB_GLOBAL: |
| case STB_WEAK: |
| return (1); |
| default: |
| return (0); |
| } |
| default: |
| return (0); |
| } |
| } |
| |
| /* |
| * If it comes here, any symbol can be printed. |
| * (So basically, -f is no-op.) |
| */ |
| return (1); |
| } |
| |
| #ifndef XPG4 |
| /* |
| * -u flag specified |
| */ |
| static void |
| print_with_uflag(SYM *sym_data, char *filename) |
| { |
| if ((sym_data->shndx == SHN_UNDEF) && (strlen(sym_data->name))) { |
| if (!r_flag) { |
| if (R_flag) { |
| if (archive_name != (char *)0) |
| (void) printf(" %s:%s:%s\n", |
| archive_name, filename, |
| sym_data->name); |
| else |
| (void) printf(" %s:%s\n", |
| filename, sym_data->name); |
| } |
| else |
| (void) printf(" %s\n", sym_data->name); |
| } |
| else |
| (void) printf(" %s:%s\n", filename, sym_data->name); |
| } |
| } |
| #endif |
| |
| /* |
| * Print a symbol type representation suitable for the -p or -P formats. |
| */ |
| static void |
| print_brief_sym_type(Elf *elf_file, unsigned int shstrndx, SYM *sym_data) |
| { |
| const char *sym_key = NULL; |
| |
| if ((sym_data->shndx == SHN_UNDEF) && (strlen(sym_data->name))) |
| sym_key = UNDEFINED; |
| else if (sym_data->type == STT_SPARC_REGISTER) { |
| switch (sym_data->bind) { |
| case STB_LOCAL : sym_key = REG_LOCL; |
| break; |
| case STB_GLOBAL : sym_key = REG_GLOB; |
| break; |
| case STB_WEAK : sym_key = REG_WEAK; |
| break; |
| default : sym_key = REG_GLOB; |
| break; |
| } |
| } else if (((sym_data->flags & FLG_SYM_SPECSEC) == 0) && |
| is_bss_section((int)sym_data->shndx, elf_file, shstrndx)) { |
| switch (sym_data->bind) { |
| case STB_LOCAL : sym_key = BSS_LOCL; |
| break; |
| case STB_GLOBAL : sym_key = BSS_GLOB; |
| break; |
| case STB_WEAK : sym_key = BSS_WEAK; |
| break; |
| default : sym_key = BSS_GLOB; |
| break; |
| } |
| |
| } else { |
| sym_key = lookup(sym_data->type, sym_data->bind); |
| } |
| |
| if (sym_key != NULL) { |
| if (!l_flag) |
| (void) printf("%c ", sym_key[0]); |
| else |
| (void) printf("%-3s", sym_key); |
| } else { |
| if (!l_flag) |
| (void) printf("%-2d", sym_data->type); |
| else |
| (void) printf("%-3d", sym_data->type); |
| } |
| } |
| |
| /* |
| * -p flag specified |
| */ |
| static void |
| print_with_pflag(int ndigits, Elf *elf_file, unsigned int shstrndx, |
| SYM *sym_data, char *filename) |
| { |
| const char * const fmt[] = { |
| "%.*llu ", /* FMT_T_DEC */ |
| "0x%.*llx ", /* FMT_T_HEX */ |
| "0%.*llo " /* FMT_T_OCT */ |
| }; |
| |
| if (is_sym_print(sym_data) != 1) |
| return; |
| /* |
| * -A header |
| */ |
| if (A_flag != 0) |
| (void) printf("%s", A_header); |
| |
| /* |
| * Symbol Value. |
| * (hex/octal/decimal) |
| */ |
| (void) printf(fmt[fmt_flag], ndigits, EC_ADDR(sym_data->value)); |
| |
| |
| /* |
| * Symbol Type. |
| */ |
| print_brief_sym_type(elf_file, shstrndx, sym_data); |
| |
| if (!r_flag) { |
| if (R_flag) { |
| if (archive_name != (char *)0) |
| (void) printf("%s:%s:%s\n", archive_name, |
| filename, sym_data->name); |
| else |
| (void) printf("%s:%s\n", filename, |
| sym_data->name); |
| } |
| else |
| (void) printf("%s\n", sym_data->name); |
| } |
| else |
| (void) printf("%s:%s\n", filename, sym_data->name); |
| } |
| |
| /* |
| * -P flag specified |
| */ |
| static void |
| print_with_Pflag(int ndigits, Elf *elf_file, unsigned int shstrndx, |
| SYM *sym_data) |
| { |
| #define SYM_LEN 10 |
| char sym_name[SYM_LEN+1]; |
| size_t len; |
| const char * const fmt[] = { |
| "%*llu %*llu \n", /* FMT_T_DEC */ |
| "%*llx %*llx \n", /* FMT_T_HEX */ |
| "%*llo %*llo \n" /* FMT_T_OCT */ |
| }; |
| |
| if (is_sym_print(sym_data) != 1) |
| return; |
| /* |
| * -A header |
| */ |
| if (A_flag != 0) |
| (void) printf("%s", A_header); |
| |
| /* |
| * Symbol name |
| */ |
| len = strlen(sym_data->name); |
| if (len >= SYM_LEN) |
| (void) printf("%s ", sym_data->name); |
| else { |
| (void) sprintf(sym_name, "%-10s", sym_data->name); |
| (void) printf("%s ", sym_name); |
| } |
| |
| /* |
| * Symbol Type. |
| */ |
| print_brief_sym_type(elf_file, shstrndx, sym_data); |
| |
| /* |
| * Symbol Value & size |
| * (hex/octal/decimal) |
| */ |
| (void) printf(fmt[fmt_flag], ndigits, EC_ADDR(sym_data->value), |
| ndigits, EC_XWORD(sym_data->size)); |
| } |
| |
| /* |
| * other flags specified |
| */ |
| static void |
| print_with_otherflags(int ndigits, Elf *elf_file, unsigned int shstrndx, |
| SYM *sym_data, char *filename) |
| { |
| const char * const fmt_value_size[] = { |
| "%*llu|%*lld|", /* FMT_T_DEC */ |
| "0x%.*llx|0x%.*llx|", /* FMT_T_HEX */ |
| "0%.*llo|0%.*llo|" /* FMT_T_OCT */ |
| }; |
| const char * const fmt_int[] = { |
| "%-5d", /* FMT_T_DEC */ |
| "%#-5x", /* FMT_T_HEX */ |
| "%#-5o" /* FMT_T_OCT */ |
| }; |
| |
| if (is_sym_print(sym_data) != 1) |
| return; |
| (void) printf("%s", A_header); |
| (void) printf("[%d]\t|", sym_data->indx); |
| (void) printf(fmt_value_size[fmt_flag], ndigits, |
| EC_ADDR(sym_data->value), ndigits, EC_XWORD(sym_data->size)); |
| |
| switch (sym_data->type) { |
| case STT_NOTYPE:(void) printf("%-5s", "NOTY"); break; |
| case STT_OBJECT:(void) printf("%-5s", "OBJT"); break; |
| case STT_FUNC: (void) printf("%-5s", "FUNC"); break; |
| case STT_SECTION:(void) printf("%-5s", "SECT"); break; |
| case STT_FILE: (void) printf("%-5s", "FILE"); break; |
| case STT_COMMON: (void) printf("%-5s", "COMM"); break; |
| case STT_TLS: (void) printf("%-5s", "TLS "); break; |
| case STT_SPARC_REGISTER: (void) printf("%-5s", "REGI"); break; |
| default: |
| (void) printf(fmt_int[fmt_flag], sym_data->type); |
| } |
| (void) printf("|"); |
| switch (sym_data->bind) { |
| case STB_LOCAL: (void) printf("%-5s", "LOCL"); break; |
| case STB_GLOBAL:(void) printf("%-5s", "GLOB"); break; |
| case STB_WEAK: (void) printf("%-5s", "WEAK"); break; |
| default: |
| (void) printf("%-5d", sym_data->bind); |
| (void) printf(fmt_int[fmt_flag], sym_data->bind); |
| } |
| (void) printf("|"); |
| (void) printf(fmt_int[fmt_flag], sym_data->other); |
| (void) printf("|"); |
| |
| if (sym_data->shndx == SHN_UNDEF) { |
| if (!s_flag) |
| (void) printf("%-7s", "UNDEF"); |
| else |
| (void) printf("%-14s", "UNDEF"); |
| } else if (sym_data->shndx == SHN_SUNW_IGNORE) { |
| if (!s_flag) |
| (void) printf("%-7s", "IGNORE"); |
| else |
| (void) printf("%-14s", "IGNORE"); |
| } else if ((sym_data->flags & FLG_SYM_SPECSEC) && |
| (sym_data->shndx == SHN_ABS)) { |
| if (!s_flag) |
| (void) printf("%-7s", "ABS"); |
| else |
| (void) printf("%-14s", "ABS"); |
| } else if ((sym_data->flags & FLG_SYM_SPECSEC) && |
| (sym_data->shndx == SHN_COMMON)) { |
| if (!s_flag) |
| (void) printf("%-7s", "COMMON"); |
| else |
| (void) printf("%-14s", "COMMON"); |
| } else { |
| if (s_flag) { |
| Elf_Scn *scn = elf_getscn(elf_file, sym_data->shndx); |
| GElf_Shdr shdr; |
| |
| if ((gelf_getshdr(scn, &shdr) != 0) && |
| (shdr.sh_name != 0)) { |
| (void) printf("%-14s", |
| (char *)elf_strptr(elf_file, |
| shstrndx, shdr.sh_name)); |
| } else { |
| (void) printf("%-14d", sym_data->shndx); |
| } |
| } else { |
| (void) printf("%-7d", sym_data->shndx); |
| } |
| } |
| (void) printf("|"); |
| if (!r_flag) { |
| if (R_flag) { |
| if (archive_name != (char *)0) |
| (void) printf("%s:%s:%s\n", archive_name, |
| filename, sym_data->name); |
| else |
| (void) printf("%s:%s\n", filename, |
| sym_data->name); |
| } |
| else |
| (void) printf("%s\n", sym_data->name); |
| } |
| else |
| (void) printf("%s:%s\n", filename, sym_data->name); |
| } |
| |
| /* |
| * C++ name demangling supporting routines |
| */ |
| static const char *ctor_str = "static constructor function for %s"; |
| static const char *dtor_str = "static destructor function for %s"; |
| static const char *ptbl_str = "pointer to the virtual table vector for %s"; |
| static const char *vtbl_str = "virtual table for %s"; |
| |
| /* |
| * alloc memory and create name in necessary format. |
| * Return name string |
| */ |
| static char * |
| FormatName(char *OldName, const char *NewName) |
| { |
| char *s = p_flag ? |
| "%s\n [%s]" : |
| "%s\n\t\t\t\t\t\t [%s]"; |
| size_t length = strlen(s)+strlen(NewName)+strlen(OldName)-3; |
| char *hold = OldName; |
| OldName = malloc(length); |
| /*LINTED*/ |
| (void) snprintf(OldName, length, s, NewName, hold); |
| return (OldName); |
| } |
| |
| |
| /* |
| * Return 1 when s is an exotic name, 0 otherwise. s remains unchanged, |
| * the exotic name, if exists, is saved in d_buf. |
| */ |
| static int |
| exotic(const char *in_str) |
| { |
| static char *buff = 0; |
| static size_t buf_size; |
| |
| size_t sym_len = strlen(in_str) + 1; |
| int tag = 0; |
| char *s; |
| |
| /* |
| * We will need to modify the symbol (in_str) as we are analyzing it, |
| * so copy it into a buffer so that we can play around with it. |
| */ |
| if (buff == NULL) { |
| buff = malloc(DEF_MAX_SYM_SIZE); |
| buf_size = DEF_MAX_SYM_SIZE; |
| } |
| |
| if (sym_len > buf_size) { |
| if (buff) |
| free(buff); |
| buff = malloc(sym_len); |
| buf_size = sym_len; |
| } |
| |
| if (buff == NULL) { |
| (void) fprintf(stderr, gettext( |
| "%s: cannot allocate memory\n"), prog_name); |
| exit(NOALLOC); |
| } |
| s = strcpy(buff, in_str); |
| |
| |
| if (strncmp(s, "__sti__", 7) == 0) { |
| s += 7; tag = 1; |
| parse_fn_and_print(ctor_str, s); |
| } else if (strncmp(s, "__std__", 7) == 0) { |
| s += 7; tag = 1; |
| parse_fn_and_print(dtor_str, s); |
| } else if (strncmp(s, "__vtbl__", 8) == 0) { |
| s += 8; tag = 1; |
| parsename(s); |
| (void) sprintf(d_buf, vtbl_str, p_buf); |
| } else if (strncmp(s, "__ptbl_vec__", 12) == 0) { |
| s += 12; tag = 1; |
| parse_fn_and_print(ptbl_str, s); |
| } |
| return (tag); |
| } |
| |
| void |
| parsename(char *s) |
| { |
| register int len; |
| char c, *orig = s; |
| *p_buf = '\0'; |
| (void) strcat(p_buf, "class "); |
| while (isdigit(*s)) s++; |
| c = *s; |
| *s = '\0'; |
| len = atoi(orig); |
| *s = c; |
| if (*(s+len) == '\0') { /* only one class name */ |
| (void) strcat(p_buf, s); |
| return; |
| } else |
| { /* two classname %drootname__%dchildname */ |
| char *root, *child, *child_len_p; |
| int child_len; |
| root = s; |
| child = s + len + 2; |
| child_len_p = child; |
| if (!isdigit(*child)) { |
| /* ptbl file name */ |
| /* %drootname__%filename */ |
| /* kludge for getting rid of '_' in file name */ |
| char *p; |
| c = *(root + len); |
| *(root + len) = '\0'; |
| (void) strcat(p_buf, root); |
| *(root + len) = c; |
| (void) strcat(p_buf, " in "); |
| for (p = child; *p != '_'; ++p) |
| ; |
| c = *p; |
| *p = '.'; |
| (void) strcat(p_buf, child); |
| *p = c; |
| return; |
| } |
| |
| while (isdigit(*child)) |
| child++; |
| c = *child; |
| *child = '\0'; |
| child_len = atoi(child_len_p); |
| *child = c; |
| if (*(child + child_len) == '\0') { |
| (void) strcat(p_buf, child); |
| (void) strcat(p_buf, " derived from "); |
| c = *(root + len); |
| *(root + len) = '\0'; |
| (void) strcat(p_buf, root); |
| *(root + len) = c; |
| return; |
| } else { |
| /* %drootname__%dchildname__filename */ |
| /* kludge for getting rid of '_' in file name */ |
| char *p; |
| c = *(child + child_len); |
| *(child + child_len) = '\0'; |
| (void) strcat(p_buf, child); |
| *(child+child_len) = c; |
| (void) strcat(p_buf, " derived from "); |
| c = *(root + len); |
| *(root + len) = '\0'; |
| (void) strcat(p_buf, root); |
| *(root + len) = c; |
| (void) strcat(p_buf, " in "); |
| for (p = child + child_len + 2; *p != '_'; ++p) |
| ; |
| c = *p; |
| *p = '.'; |
| (void) strcat(p_buf, child + child_len + 2); |
| *p = c; |
| return; |
| } |
| } |
| } |
| |
| void |
| parse_fn_and_print(const char *str, char *s) |
| { |
| char c, *p1, *p2; |
| int yes = 1; |
| |
| if ((p1 = p2 = strstr(s, "_c_")) == NULL) |
| if ((p1 = p2 = strstr(s, "_C_")) == NULL) |
| if ((p1 = p2 = strstr(s, "_cc_")) == NULL) |
| if ((p1 = p2 = strstr(s, "_cxx_")) == NULL) |
| if ((p1 = p2 = strstr(s, "_h_")) == |
| NULL) |
| yes = 0; |
| else |
| p2 += 2; |
| else |
| p2 += 4; |
| else |
| p2 += 3; |
| else |
| p2 += 2; |
| else |
| p2 += 2; |
| |
| if (yes) { |
| *p1 = '.'; |
| c = *p2; |
| *p2 = '\0'; |
| } |
| |
| for (s = p1; *s != '_'; --s) |
| ; |
| ++s; |
| |
| (void) sprintf(d_buf, str, s); |
| |
| if (yes) { |
| *p1 = '_'; |
| *p2 = c; |
| } |
| } |