/*
* 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
* 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
*
*/
#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 */
#include <fcntl.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;
int type;
int bind;
unsigned char other;
unsigned int shndx;
} SYM;
/* (SHN_ABS, SHN_COMMON, ...) */
/*
* 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 {
/* The following are used as array indices */
FMT_T_DEC = 0,
} 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.
*/
static int /* flags: ?_flag corresponds to ? option */
/* to each symbol name */
/* object file name to each symbol */
static char *prog_name;
static char *archive_name = (char *)0;
static int errflag = 0;
static void usage();
static void each_file(char *);
static void get_symtab(Elf *, char *);
unsigned int);
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 parsename(char *);
static void parse_fn_and_print(const char *, char *);
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
{
int optchar;
#ifndef XPG4
/*
* Check for a binary that better fits this architecture.
*/
#endif
/* table of keyletters for use with -p and -P options */
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
switch (optchar) {
else
"%s: -x or -t set, -o ignored\n"),
break;
else
"%s: -o or -t set, -x ignored\n"),
break;
break;
case 'v': if (!n_flag)
v_flag = 1;
else
"%s: -n set, -v ignored\n"),
break;
case 'n': if (!v_flag)
n_flag = 1;
else
"%s: -v set, -n ignored\n"),
break;
u_flag = 1;
else
"%s: -e or -g set, -u ignored\n"),
break;
e_flag = 1;
else
"%s: -u or -g set, -e ignored\n"),
break;
g_flag = 1;
else
"%s: -u or -e set, -g ignored\n"),
break;
case 'r': if (R_flag) {
R_flag = 0;
"%s: -r set, -R ignored\n"),
}
r_flag = 1;
break;
break;
"nm: -P set. -p ignored\n"));
} else
p_flag = 1;
break;
"nm: -p set. -P ignored\n"));
} else
P_flag = 1;
break;
break;
"nm: -D set. -L ignored\n"));
} else
L_flag = 1;
break;
"nm: -L set. -D ignored\n"));
} else
D_flag = 1;
break;
case 'C':
C_flag = 1;
break;
break;
(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
"%s: -r set, -R ignored\n"),
break;
case 'T':
break;
} else {
}
if (new_fmt_flag == FMT_T_NONE) {
errflag += 1;
"nm: -t requires radix value (d, o, x): %s\n"), optarg);
} else if (COMPAT_FMT_FLAG(new_fmt_flag)) {
} else {
"nm: -t or -o or -x set. -t ignored.\n"));
}
break;
"nm: %c requires operand\n"), optopt);
break;
break;
default: break;
}
}
usage();
}
}
/*
* 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)
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()
{
"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
{
int fd;
errno = 0;
errflag++;
return;
}
"%s: %s: libelf is out of date\n"),
}
errflag++;
return;
}
cmd = ELF_C_READ;
errflag++;
return;
}
} else {
#ifndef XPG4
/*
* u_flag is specified.
*/
if (p_flag)
else
"\n\nUndefined symbols from %s:\n\n"),
filename);
#else
#endif
{
if (p_flag)
else {
if (A_flag != 0)
(void) printf("\n\n%s%s:\n",
else
(void) printf("\n\n%s:\n",
filename);
}
}
archive_name = (char *)0;
} else {
"%s: %s: invalid file type\n"),
errflag++;
}
}
}
/*
* 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
{
return;
}
}
/*
* 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 *
{
return (NULL);
}
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
{
"%s: %s: cannot get e_shstrndx\n"),
return;
}
/* get section header string table */
"%s: %s: cannot get string table\n"),
return;
}
"%s: %s: no data in string table\n"),
return;
}
if (D_flag)
else if (L_flag)
else
scn = 0;
return;
}
}
} /* 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
{
cmd = ELF_C_READ;
return;
}
continue;
}
if (p_flag)
(void) printf("\n\n%s[%s]:\n",
else {
if (A_flag != 0)
(void) printf("\n\n%s%s[%s]:\n",
else
(void) printf("\n\n%s[%s]:\n",
}
}
} else {
"%s: %s: invalid file type\n"),
errflag++;
continue;
}
} /* end while */
}
static void print_header(int);
#ifndef XPG4
static void print_with_uflag(SYM *, char *);
#endif
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
{
SYM *s;
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 *= 2;
/*
* print header
*/
/*
* get symbol table data
*/
gettext("%s: %s: no symbol table data\n"),
return;
}
/*
* translate symbol table data
*/
(unsigned int)elf_ndxscn(p_sd));
"%s: %s: problem reading symbol data\n"),
return;
}
(int (*)(const void *, const void *))compare);
s = sym_data;
while (count > 1) {
#ifndef XPG4
if (u_flag) {
/*
* U_flag specified
*/
} else if (p_flag)
#else
if (p_flag)
#endif
else if (P_flag)
sym_data);
else
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 TRUE(1) if the given section is ".bss" for "-p" option.
* Return FALSE(0) if not ".bss" section.
*/
static int
{
char *sym_name;
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 *
{
unsigned int nosymshndx = 0;
int i;
return (NULL);
}
s = buf; /* save pointer to head of array */
/* allow to work on machines where NULL-derefs dump core */
else if (C_flag) {
const char *dn;
}
} else { /* name demangled */
}
}
else
(symshndx == 0) && (nosymshndx == 0)) {
_scn = 0;
break;
0)) != 0) {
symshndx =
break;
}
}
}
nosymshndx = 1;
}
} else {
}
} /* 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
{
if (v_flag) {
return (1);
else
} else
}
/*
* Set up a header line for -A option.
*/
static void
{
if (A_flag == 0)
return;
if (archive_name == (char *)0) {
} else {
}
}
/*
* 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
{
const char *fmt;
const char *section_title;
1, /* FMT_T_DEC: '|' */
3, /* FMT_T_HEX: '|0x' */
2, /* FMT_T_OCT: '|0' */
};
if (
#ifndef XPG4
!u_flag &&
#endif
(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)
}
}
/*
* If the symbol can be printed, then return 1.
* If the symbol can not be printed, then return 0.
*/
static int
{
/*
* If -u flag is specified,
* the symbol has to be undefined.
*/
if (u_flag != 0) {
return (1);
else
return (0);
}
/*
* If -e flag is specified,
* the symbol has to be global or static.
*/
if (e_flag != 0) {
case STT_NOTYPE:
case STT_OBJECT:
case STT_FUNC:
case STT_COMMON:
case STT_TLS:
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) {
case STT_NOTYPE:
case STT_OBJECT:
case STT_FUNC:
case STT_COMMON:
case STT_TLS:
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
char *filename
)
{
if (!r_flag) {
if (R_flag) {
if (archive_name != (char *)0)
(void) printf(" %s:%s:%s\n",
else
(void) printf(" %s:%s\n",
}
else
}
else
}
}
#endif
/*
* Print a symbol type representation suitable for the -p or -P formats.
*/
static void
{
break;
break;
break;
break;
}
break;
break;
break;
break;
}
} else {
}
if (!l_flag)
else
} else {
if (!l_flag)
else
}
}
/*
* -p flag specified
*/
static void
int ndigits,
unsigned int shstrndx,
char *filename
)
{
const char * const fmt[] = {
"%.*llu ", /* FMT_T_DEC */
"0x%.*llx ", /* FMT_T_HEX */
"0%.*llo " /* FMT_T_OCT */
};
return;
/*
* -A header
*/
if (A_flag != 0)
/*
* Symbol Value.
*/
/*
* Symbol Type.
*/
if (!r_flag) {
if (R_flag) {
if (archive_name != (char *)0)
else
}
else
}
else
}
/*
* -P flag specified
*/
static void
int ndigits,
unsigned int shstrndx,
)
{
const char * const fmt[] = {
"%*llu %*llu \n", /* FMT_T_DEC */
"%*llx %*llx \n", /* FMT_T_HEX */
"%*llo %*llo \n" /* FMT_T_OCT */
};
return;
/*
* -A header
*/
if (A_flag != 0)
/*
* Symbol name
*/
else {
}
/*
* Symbol Type.
*/
/*
* Symbol Value & size
*/
}
/*
* other flags specified
*/
static void
int ndigits,
unsigned int shstrndx,
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 */
};
return;
default:
}
(void) printf("|");
default:
}
(void) printf("|");
(void) printf("|");
if (!s_flag)
else
if (!s_flag)
else
if (!s_flag)
else
if (!s_flag)
else
} else {
if (s_flag) {
(void) printf("%-14s",
(char *)elf_strptr(elf_file,
} else {
}
} else {
}
}
(void) printf("|");
if (!r_flag) {
if (R_flag) {
if (archive_name != (char *)0)
else
}
else
}
else
}
/*
* C++ name demangling supporting routines
*/
/*
* alloc memory and create name in necessary format.
* Return name string
*/
static char *
{
char *s = p_flag ?
"%s\n [%s]" :
"%s\n\t\t\t\t\t\t [%s]";
/*LINTED*/
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
{
static char *buff = 0;
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)
}
"%s: cannot allocate memory\n"), prog_name);
}
parsename(s);
}
return (tag);
}
void
parsename(char *s)
{
register int len;
char c, *orig = s;
*p_buf = '\0';
while (isdigit(*s)) s++;
c = *s;
*s = '\0';
*s = c;
return;
} else
{ /* two classname %drootname__%dchildname */
int child_len;
root = s;
child_len_p = child;
/* ptbl file name */
/* %drootname__%filename */
/* kludge for getting rid of '_' in file name */
char *p;
for (p = child; *p != '_'; ++p)
;
c = *p;
*p = '.';
*p = c;
return;
}
child++;
c = *child;
*child = '\0';
*child = c;
return;
} else {
/* %drootname__%dchildname__filename */
/* kludge for getting rid of '_' in file name */
char *p;
;
c = *p;
*p = '.';
*p = c;
return;
}
}
}
void
{
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;
if (yes) {
*p1 = '_';
*p2 = c;
}
}