elfdump.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Dump an elf file.
*/
#include <fcntl.h>
#include <stdio.h>
#include <libelf.h>
#include <gelf.h>
#include <link.h>
#include <stdarg.h>
#include <unistd.h>
#include <libgen.h>
#include <libintl.h>
#include <locale.h>
#include <errno.h>
#include <strings.h>
#include <sys/elf_SPARC.h>
#include <sys/elf_amd64.h>
#include <debug.h>
#include <_debug.h>
#include <conv.h>
#include <msg.h>
#include <dwarf.h>
#define FLG_DYNAMIC 0x00000001
#define FLG_EHDR 0x00000002
#define FLG_INTERP 0x00000004
#define FLG_SHDR 0x00000008
#define FLG_NOTE 0x00000010
#define FLG_PHDR 0x00000020
#define FLG_RELOC 0x00000040
#define FLG_SYMBOLS 0x00000080
#define FLG_VERSIONS 0x00000100
#define FLG_HASH 0x00000200
#define FLG_GOT 0x00000400
#define FLG_SYMINFO 0x00000800
#define FLG_MOVE 0x00001000
#define FLG_GROUP 0x00002000
#define FLG_CAP 0x00004000
#define FLG_UNWIND 0x00008000
#define FLG_EVERYTHING 0x000fffff
#define IAM_SPARC(X) \
#define IAM_INTEL(X) \
(X == EM_386)
#define MAXNDXSIZE 10
typedef struct cache {
char *c_name;
} Cache;
typedef struct got_info {
/* support mixed relocations */
const char *g_symname;
} Got_info;
const char *
{
}
/*
* Determine whether a symbol name should be demangled.
*/
static const char *
{
if (flags & FLG_DEMANGLE)
return (Gelf_sym_dem(name));
else
return ((char *)name);
}
/*
* Define our own printing routine. All Elf routines referenced call upon
* this routine to carry out the actual printing.
*/
/*PRINTFLIKE1*/
void
{
}
/*
* Just like dbg_print - except that it does not insert
* a newline at the end. Can be used for printing tables
* and such.
*/
/*PRINTFLIKE1*/
void
dbg_printf(const char *format, ...)
{
}
/*
* Define our own standard error routine.
*/
static void
{
}
/*
* Focal point for verifying symbol names.
*/
static const char *
{
static int nostr;
/*
* Only print a diagnoistic regarding an empty string table once per
* input section being processed.
*/
nostr = 0;
}
/*
* Is the string table offset within range of the available strings?
*/
/*
* Do we have a empty string table?
*/
if (strs == 0) {
if (nostr == 0) {
nostr++;
}
} else {
}
/*
* Return the empty string so that the calling function can
* continue it's output diagnostics.
*/
return (MSG_INTL(MSG_STR_UNKNOWN));
}
}
/*
* Lookup a symbol and set Sym accordingly.
*
* Returns:
* 1 - symbol found
* 0 - symbol not found
*/
static int
{
if (symtab == 0)
return (0);
/*
* Determine the symbol data and number.
*/
return (0);
}
/* LINTED */
/*
* Get the associated string table section.
*/
return (0);
}
/*
* Loop through the symbol table to find a match.
*/
const char *sname;
return (0);
}
return (1);
}
}
return (0);
}
/*
* The full usage message
*/
static void
{
}
/*
* Print section headers.
*/
static void
const char *name)
{
const char *sname;
continue;
/*
* Although numerous section header entries can be zero, it's
* usually a sign of trouble if the name or type are zero.
*/
}
/*
* Use the empty string, rather than the fabricated
* name for the section output.
*/
}
/* LINTED */
}
}
static void
{
/*
* For the moment - UNWIND is only relevant for
* a AMD64 object
*/
return;
return;
}
unwind_phdr = phdr;
break;
}
}
unsigned char *data;
/*
* XX64 - this is a strmcp() just to find the gcc
* produced sections. Soon gcc should be
* settng the section type - and we'll not need
* this strcmp().
*/
MSG_SCN_FRM_SIZE) != 0) &&
MSG_SCN_FRMHDR_SIZE) != 0))
continue;
continue;
off = 0;
/*
* Is this a .eh_frame_hdr
*/
MSG_SCN_FRMHDR_SIZE) == 0)) {
ndx = 0;
fde_cnt);
}
continue;
}
/*
* Walk the Eh_frame's
*/
char *cieaugstr;
ndx = 0;
/*
* extract length in lsb format
*/
ndx += 4;
/*
* extract CIE id in lsb format
*/
ndx += 4;
/*
* A CIE record has a id of '0', otherwise
* this is a FDE entry and the 'id' is the
* CIE pointer.
*/
if (id == 0) {
cieLflag = 0;
ciePflag = 0;
cieRflag = 0;
cieZflag = 0;
ndx += 1;
ndx += 1;
if (cieaugstr[0])
cieaugndx++) {
case 'z':
&ndx);
val);
cieZflag = 1;
break;
case 'P':
ndx += 1;
break;
case 'R':
ndx += 1;
break;
case 'L':
ndx += 1;
break;
default:
break;
}
}
cnt = 0;
if ((cnt++ % 8) == 0) {
}
}
}
} else {
if (cieaugstr[0])
if (cieZflag) {
val);
}
}
cnt = 0;
if ((cnt++ % 8) == 0) {
}
}
}
}
}
}
}
/*
* this should be accompanied with a program header.
*/
static void
{
/*
*/
return;
}
break;
}
}
/*
*/
continue;
continue;
break;
}
return;
/*
*/
if (cshdr) {
/* LINTED */
/* LINTED */
return;
}
}
} else
/*
* If this object is an executable or shared object, then the
* program header.
*/
if (cphdr_off == 0)
}
}
/*
* Print the interpretor.
*/
static void
{
/*
* Determine if an interp header exists.
*/
return;
}
break;
}
}
if (iphdr_off == 0)
return;
/*
* Determine if an interp section exists.
*/
/*
* Scan sections to find a section which contains the PT_INTERP
* string. The target section can't be in a NOBITS section.
*/
continue;
break;
}
/*
* Print the interpreter string based on the offset defined in the
* program header, as this is the offset used by the kernel.
*/
if (ishdr) {
} else
/*
* If there are any inconsistences between the program header and
* section information, flag them.
*/
}
}
/*
* Print the syminfo section.
*/
static void
{
char *sname;
break;
}
}
if (syminfo == 0)
return;
/*
* Determine the symbol info data and number.
*/
return;
}
/* LINTED */
/*
* Get the data buffer of the associated dynamic section.
*/
return;
}
return;
}
/*
* Get the data buffer of the associated symbol table.
*/
return;
}
return;
}
/*
* Get the associated string table section.
*/
return;
}
return;
}
continue;
return;
}
needed = 0;
return;
}
}
}
}
/*
* Print version definition section entries.
*/
static void
const char *file)
{
char index[MAXNDXSIZE];
/*
* Obtain the name and first dependency (if any).
*/
if (vcnt)
else
/*
* Print any additional dependencies.
*/
if (vcnt) {
}
}
}
}
/*
* Print a version needed section entries.
*/
static void
const char *file)
{
/*
* Obtain the name of the needed file and the version name
* within it that we're dependent on. Note that the count
* should be at least one, otherwise this is a pretty bogus
* entry.
*/
if (vcnt)
else
/*
* Print any additional version dependencies.
*/
if (vcnt) {
}
}
}
}
/*
* Search for any verion sections - the Versym output is possibly
* used by the symbols() printing. If VERSYM is specified - then
* display the version information.
*/
static Cache *
{
Cache *versymcache = 0;
void * ver;
/*
* If this is the version symbol table simply record its
* data address for possible use in later symbol processing.
*/
continue;
}
if ((flags & FLG_VERSIONS) == 0)
continue;
continue;
/*
* Determine the version section data and number.
*/
continue;
}
continue;
}
/*
* Get the data buffer for the associated string table.
*/
continue;
}
}
}
return (versymcache);
}
/*
* Search for and process any symbol tables.
*/
static void
{
continue;
continue;
/*
* Determine the symbol data and number.
*/
continue;
}
/* LINTED */
/*
* Get the associated string table section.
*/
continue;
}
/*
* Determine if there is a associated Versym section
* with this Symbol Table.
*/
else
versym = 0;
/*
* Loop through the symbol tables entries.
*/
symshndx = 0;
nosymshndx = 0;
nosyminshndx = 0;
char index[MAXNDXSIZE];
char *sec;
const char *sname;
int verndx;
break;
}
/*
* If we are using extended symbol indexes, find the
* corresponding SHN_SYMTAB_SHNDX table.
*/
(symshndx == 0) && (nosymshndx == 0)) {
SHT_SYMTAB_SHNDX) ||
continue;
if (shdr->sh_entsize)
/* LINTED */
nosyminshndx = (uint_t)
if (nosyminshndx == 0)
continue;
break;
}
if (symshndx == 0)
nosymshndx = 1;
}
/* LINTED */
tshdr = 0;
if (is_core)
if (symshndx) {
if (_cnt > nosyminshndx) {
} else if ((_symshndx =
} else {
}
} else {
}
}
/*
* If versioning is available display the
* version index.
*/
if (versym)
else
verndx = 0;
/*
* Error checking for TLS.
*/
if (tshdr &&
}
}
/*
* If a symbol has size, then make sure the section it
* references is appropriate. Note, UNDEF symbols that
* have a size, have been known to exist - ignore them.
*/
}
sname);
}
}
}
/*
* Search for and process any relocation sections.
*/
static void
{
int ndx;
char *sname;
continue;
continue;
/*
* Decide entry size
*/
else
}
/*
* Determine the number of relocations available.
*/
continue;
}
/*
* Get the data buffer for the associated symbol table. Note
* that we've been known to create static binaries containing
* relocations against weak symbols, if these get stripped the
* relocation records can't make symbolic references.
*/
continue;
}
continue;
}
/*
* Get the associated string table section.
*/
continue;
}
/*
* Loop through the relocation entries.
*/
if (flags & FLG_LONGNAME)
else
} else {
if (flags & FLG_LONGNAME)
else
}
/* LINTED */
const char *_name;
/*
* Determine the symbol with which this relocation is
* associated. If the symbol represents a section
* offset construct an appropriate string.
*/
&rela);
} else {
}
/* LINTED */
/* LINTED */
file, elf_errmsg(0));
} else {
if (flags & FLG_LONGNAME)
else
} else {
/* LINTED */
}
}
((r_type != R_SPARC_NONE) &&
(r_type != R_SPARC_REGISTER) &&
(r_type != R_SPARC_RELATIVE))) ||
((r_type != R_386_NONE) &&
(r_type != R_386_RELATIVE)))))) {
/* LINTED */
}
}
}
}
/*
* Search for and process a .dynamic section.
*/
static void
{
int ndx;
continue;
/*
* Get the associated string table section.
*/
continue;
}
/* LINTED */
const char *name;
break;
/*
* Print the information numerically, and if possible
* as a string.
*/
/* LINTED */
/* LINTED */
/* LINTED */
/* LINTED */
else
}
}
}
/*
* Search for and process a MOVE section.
*/
static void
{
int ndx;
const char *fmt;
char *sname;
continue;
continue;
/*
* Determine the move data and number.
*/
continue;
}
/*
* Get the data buffer for the associated symbol table.
*/
continue;
}
continue;
}
/*
* Get the associated string table section.
*/
continue;
}
continue;
}
/* LINTED */
const char *name;
break;
}
/*
* Check for null entries
*/
continue;
}
/* LINTED */
continue;
}
if (gelf_getsym(dsyms,
/* LINTED */
file, elf_errmsg(0));
} else {
if (flags & FLG_LONGNAME)
else
} else {
/* LINTED */
}
}
/*
* Additional sanity check.
*/
if (!((shndx == SHN_COMMON) ||
}
/* LINTED */
}
}
}
/*
* Traverse a note section analyzing each note information block.
* The data buffers size is used to validate references before they are made,
* and is decremented as each element is processed.
*/
void
{
/*
* Print out a single `note' information block.
*/
while (size > 0) {
/*
* Make sure we can at least reference the 3 initial entries
* (4-byte words) of the note information block.
*/
else {
return;
}
/*
* Make sure any specified name string can be referenced.
*/
else {
return;
}
}
/*
* Make sure any specified descriptor can be referenced.
*/
/*
* If namesz isn't a 4-byte multiple, account for any
* padding that must exist before the descriptor.
*/
}
else {
return;
}
}
if (namesz) {
/*
* Since the name string may have 'null' bytes
* in it (ia32 .string) - we just write the
* whole stream in a single fwrite.
*/
~(sizeof (Word) - 1));
/* LINTED */
}
/*
* If multiple information blocks exist within a .note section
* account for any padding that must exist before the next
* information block.
*/
}
if (descsz) {
/*
* Dump descriptor bytes.
*/
tok);
str += 3;
if (++byte == 4) {
word++;
byte = 0;
}
if (word == 4) {
*str = '\0';
word = 0;
ndx += 16;
}
}
*str = '\0';
}
/* LINTED */
}
}
}
/*
* Search for and process a .note section.
*/
static void
{
/*
* Otherwise look for any .note sections.
*/
continue;
continue;
/*
* As these sections are often hand rolled, make sure they're
* properly aligned before proceeding.
*/
continue;
}
/* LINTED */
}
}
#define MAXCOUNT 500
static void
{
char number[MAXNDXSIZE];
char *sname;
continue;
continue;
/*
* Determine the hash table data and size.
*/
continue;
}
hash += 2;
/*
* Get the data buffer for the associated symbol table.
*/
continue;
}
continue;
}
/*
* Get the associated string table section.
*/
continue;
}
/*
* Loop through the hash buckets, printing the appropriate
* symbols.
*/
const char *_str;
char _number[MAXNDXSIZE];
if (*hash == 0) {
count[0]++;
continue;
}
/* LINTED */
file, elf_errmsg(0));
} else {
}
/* LINTED */
/*
* Determine if this string is in the correct bucket.
*/
/* LINTED */
}
/*
* Determine if any other symbols are chained to this
* bucket.
*/
_cnt = 1;
while (_ndx) {
/* LINTED */
file, elf_errmsg(0));
} else
_cnt++;
/*
* Determine if this string is in the correct
* bucket.
*/
/* LINTED */
}
}
/* LINTED */
} else
}
break;
}
/*
* Print out the count information.
*/
continue;
/* LINTED */
(int)_cnt);
/* LINTED */
/* LINTED */
}
if (cnt) {
/* LINTED */
(int)bkts);
/* LINTED */
}
}
static void
{
const char *symname;
continue;
continue;
continue;
}
}
continue;
}
continue;
}
flgstrbuf[0] = '[';
if (grpdata[0] & GRP_COMDAT) {
}
if ((grpdata[0] & ~GRP_COMDAT) != 0) {
(MSG_GRP_COMDAT_SIZE + 10),
}
}
file, elf_errmsg(0));
}
flags);
char index[MAXNDXSIZE];
const char *sname;
} else {
}
}
}
}
static void
{
char *gotdata;
/*
* First we find the got
*/
break;
}
}
if (!gotcache)
return;
return;
}
/*
* Some architectures don't properly set the sh_entsize
* for the GOT table. If it's not set we will default
* to a size of a pointer.
*/
gentsize = sizeof (GElf_Xword);
else
}
/* LINTED */
return;
}
/*
* Now we scan through all the sections looking for any relocations
* that may be against the GOT. Since these may not be isolated to a
* .rel[a].got section we check them all.
* While scanning sections save the symbol table entry (a symtab
* overriding a dynsym) so that we can lookup _GLOBAL_OFFSET_TABLE_.
*/
int ndx;
char *sname;
continue;
}
continue;
}
continue;
/*
* Determine the relocation data and number.
*/
continue;
}
/*
* Get the data buffer for the associated symbol table.
*/
continue;
}
continue;
}
/*
* Get the associated string table section.
*/
continue;
}
/* LINTED */
void *relret;
&rela);
} else {
}
break;
}
/* LINTED */
/*
* Only pay attention to relocations against the GOT.
*/
continue;
/* LINTED */
/* LINTED */
continue;
}
/* LINTED */
file, elf_errmsg(0));
} else {
}
}
}
else
/* LINTED */
/* LINTED */
gotndx));
else
/* LINTED */
}
}
void
{
}
static void
{
char *names = 0;
return;
}
return;
}
/* LINTED */
return;
}
return;
}
} else
_shdr0 = 0;
/*
* Print the elf header.
*/
/*
* Print the program headers.
*/
return;
}
}
}
/*
* If there are no sections (core files), or if we don't want
* any section information we might as well return now.
*/
return;
}
/*
* Obtain the .shstrtab data buffer to provide the required section
* name strings.
*/
/* LINTED */
(int)elf_ndxscn(scn));
}
/*
* Fill in the cache descriptor with information for each section.
*/
return;
}
*cache = _cache_init;
_cache++;
/* LINTED */
(int)elf_ndxscn(scn));
}
/* LINTED */
else {
/*
* If there exists no shstrtab data, or a section header
* has no name (an invalid index of 0), then compose a
* name for each section.
*/
char scnndxnm[100];
cnt);
/*
* Although we have a valid shstrtab section inform the
* user if this section name index exceeds the shstrtab
* data.
*/
if (names &&
/* LINTED */
}
return;
}
}
/* LINTED */
(int)elf_ndxscn(scn));
}
/*
* Do we wish to write the section out?
*/
}
}
if (flags & FLG_INTERP)
if (flags & FLG_SYMBOLS)
if (flags & FLG_SYMINFO)
if (flags & FLG_DYNAMIC)
if (flags & FLG_CHECKSUM)
if (flags & FLG_UNWIND)
}
static void
int wfd)
{
/*
* Determine if the archive sysmbol table itself is required.
*/
/*
* Get the archive symbol table.
*/
/*
* The arsym could be 0 even though there was no error.
* Print the error message only when there was
* real error from elf_getarsym().
*/
return;
}
}
/*
* Print the archive symbol table only when the archive symbol
* table exists and it was requested to print.
*/
if (arsym) {
char index[MAXNDXSIZE];
/*
* Print out all the symbol entries.
*/
/*
* For each object obtain an elf descriptor so that we
* can establish the members name. Note, we have had
* archives where the archive header has not been
* obtainable so be lenient with errors.
*/
if (_elf)
arhdr = 0;
ELF_C_READ, elf)) == 0) {
arhdr = 0;
arhdr = 0;
}
if (offset == 0)
}
/* LINTED */
MSG_INTL(MSG_STR_NULL)));
else
/* LINTED */
}
if (_elf)
/*
* If we only need the archive symbol table return.
*/
return;
/*
* Reset elf descriptor in preparation for processing each
* member.
*/
if (offset)
}
/*
* Process each object within the archive.
*/
char name[MAXPATHLEN];
return;
}
case ELF_K_AR:
break;
case ELF_K_ELF:
break;
default:
break;
}
}
}
}
int
{
/*
* If we're on a 64-bit kernel, try to exec a full 64-bit version of
* the binary. If successful, conv_check_native() won't return.
*/
/*
* Establish locale.
*/
opterr = 0;
switch (var) {
case 'C':
flags |= FLG_DEMANGLE;
break;
case 'c':
break;
case 'd':
flags |= FLG_DYNAMIC;
break;
case 'e':
break;
case 'G':
break;
case 'g':
break;
case 'H':
break;
case 'h':
break;
case 'i':
flags |= FLG_INTERP;
break;
case 'k':
flags |= FLG_CHECKSUM;
break;
case 'l':
flags |= FLG_LONGNAME;
break;
case 'm':
break;
case 'N':
break;
case 'n':
break;
case 'p':
break;
case 'r':
break;
case 's':
flags |= FLG_SYMBOLS;
break;
case 'u':
flags |= FLG_UNWIND;
break;
case 'v':
flags |= FLG_VERSIONS;
break;
case 'w':
break;
case 'y':
flags |= FLG_SYMINFO;
break;
case '?':
detail_usage();
return (1);
default:
break;
}
}
/*
* Validate any arguments.
*/
if (flags == 0) {
return (1);
}
}
return (1);
}
/*
* If the -C option is used by itself, report an error since the option
* has no use without other symbol name generating options.
*
* If the -l option is used by itself, report an error.
*/
if (flags & FLG_DEMANGLE)
if (flags & FLG_LONGNAME)
return (1);
}
/*
* If the -l/-C option is specified, set up the liblddbg.so.
*/
if (flags & FLG_LONGNAME)
if (flags & FLG_DEMANGLE)
if (dbg_flags)
/*
* If the -w option has indicated an output file open it. It's
* arguable whether this option has much use when multiple files are
* being processed.
*/
if (wname) {
0666)) < 0) {
wfd = 0;
}
}
/*
* Open the input file and initialize the elf interface.
*/
continue;
}
(void) elf_version(EV_CURRENT);
continue;
}
if (var > 1)
case ELF_K_AR:
break;
case ELF_K_ELF:
break;
default:
break;
}
}
if (wfd)
return (0);
}