nm.c revision b9bd317cda1afb3a01f4812de73e8cec888cbbd7
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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(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) {
(void) fprintf(stderr, gettext(
"nm: illegal format '%s' for -t is specified. -t ignored.\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 [-APvChlnV] [-efox] [-r | -R] [-g | -u] [-t format] 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_getshstrndx(elf_file, &shstrndx) == 0) {
(void) fprintf(stderr, gettext(
"%s: %s: could not 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: could not 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 ((int)strncmp(p_ar->ar_name, "/", 1) == 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 */
}
/*
* 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 */
};
static void print_header(int);
int ndigits;
#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 *);
/*
* 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) printf(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, "%s: cannot calloc space\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;
char *name = (char *)elf_strptr(elf, link, sym.st_name);
dn = conv_demangle_name(name);
if (strcmp(dn, name) == 0) { /* Not demangled */
if (exotic(name)) {
name = FormatName(name, d_buf);
}
} else { /* name demangled */
name = FormatName(name, dn);
}
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
/*
* -p flag specified
*/
static void
print_with_pflag(
int ndigits,
Elf *elf_file,
unsigned int shstrndx,
SYM *sym_data,
char *filename
)
{
char *sym_key = NULL;
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.
*/
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);
}
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
)
{
char *sym_key = NULL;
#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.
*/
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);
}
/*
* 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(char *s)
{
int tag = 0;
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;
size_t sym_len = strlen(s);
int yes = 1;
static char *buff = 0;
static size_t buf_size;
/*
* We will need to modify the symbol (s) 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 malloc space\n"), prog_name);
exit(NOALLOC);
}
s = strcpy(buff, s);
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;
}
}