/* * 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 * Copyright 2019, Joyent, Inc. */ #include #include #include #include #include #include #include /* exit return codes */ #define NOARGS 1 #define BADELF 2 #define NOALLOC 3 #include #include #include #include #include #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 ":APDoxhvniursplLCVefgRTt:" /* 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 */ i_flag = 0, /* don't sort symbols */ 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 && !i_flag) { v_flag = 1; } else { (void) fprintf(stderr, gettext( "%s: -n or -i set, -v ignored\n"), prog_name); } break; case 'n': if (!v_flag && !i_flag) { n_flag = 1; } else { (void) fprintf(stderr, gettext( "%s: -v or -i set, -n ignored\n"), prog_name); } break; case 'i': if (!n_flag && !v_flag) { i_flag = 1; } else { (void) fprintf(stderr, gettext( "%s: -n or -v set, -i 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 [-ACDhiLlnPpRrsTVv] [-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 == 0) && (P_flag == 0)) { #else if ((h_flag == 0) && (P_flag == 0)) { #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 == 0) && (P_flag == 0)) { 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; } if (i_flag == 0) { 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 = '\0', *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; } }