public inbox for gas2@sourceware.org
 help / color / mirror / Atom feed
* Elfdump
@ 1994-11-11  8:39 Michael Meissner
  1994-11-11 13:42 ` Elfdump Richard Stallman
  0 siblings, 1 reply; 5+ messages in thread
From: Michael Meissner @ 1994-11-11  8:39 UTC (permalink / raw)
  To: gas2, bfd

I wrote the following ELF dumper in order to learn ELF, and thought
others might find it useful.  It is more specific than objdump (and it
especially helped when the BFD support for ELF was weak).

/*
 * COPYRIGHT NOTICE
 * 
 * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. 
 * 
 * Permission is hereby granted to use, copy, modify and freely distribute
 * the software in this file and its documentation for any purpose without
 * fee, provided that the above copyright notice appears in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.  Further, provided that the name of Open
 * Software Foundation, Inc. ("OSF") not be used in advertising or
 * publicity pertaining to distribution of the software without prior
 * written permission from OSF.  OSF makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */
/*
 * ELF object file dumper.
 */
/*
 * HISTORY
 * $Log: elfdump.c,v $
 * Revision 1.1.2.32  1994/04/08  17:10:34  meissner
 * 	Correctly print out .lprofil sections when a double word count is used.
 * 	[1994/04/08  17:10:25  meissner]
 *
 * Revision 1.1.2.31  1994/04/07  13:56:54  meissner
 * 	Range check section section index in get_section_name.
 * 	Add program header regions to vm/file map printout.
 * 	[1994/04/07  13:56:44  meissner]
 * 
 * Revision 1.1.2.30  1994/03/17  13:28:57  meissner
 * 	Cut down on more spaces in output.
 * 	Print section name instead of number in symbol.
 * 	If -e, add linker generated symbols in vm layout.
 * 	Indicate where there are holes in the file/vm layout.
 * 	Don't print profil buckets that are 0.
 * 	[1994/03/17  13:28:49  meissner]
 * 
 * Revision 1.1.2.29  1994/03/16  22:27:11  meissner
 * 	Print out layout information for the ELF file and virtual memory.
 * 	Check layouts printed for overlaps.
 * 	Print out end offsets in the program headers.
 * 	Lowercase section flags.
 * 	Cut down on symbol line length.
 * 	[1994/03/16  22:27:02  meissner]
 * 
 * Revision 1.1.2.28  1994/03/16  18:25:24  meissner
 * 	Align summary fields of section headers.
 * 	Print out end+1 offset in section headers.
 * 	Always print all section header fields.
 * 	Skip profiling sections if -p.
 * 	[1994/03/16  18:25:14  meissner]
 * 
 * Revision 1.1.2.27  1994/03/11  22:35:01  chasb
 * 	Expand Copyrights
 * 	[1994/03/09  19:43:03  chasb]
 * 
 * Revision 1.1.2.26  1994/02/28  21:36:34  meissner
 * 	Correctly print out profil lowpc.
 * 	[1994/02/28  21:36:24  meissner]
 * 
 * Revision 1.1.2.25  1994/02/25  16:55:19  meissner
 * 	Add support for printing profile stats on OSF/1.
 * 	Allow unsigned long in addition to int for profil sizes.
 * 	[1994/02/25  16:55:11  meissner]
 * 
 * Revision 1.1.2.24  1993/12/23  01:18:10  meissner
 * 	On OSF/1 print the uarea for profiling ELF files.
 * 	[1993/12/23  01:18:03  meissner]
 * 
 * Revision 1.1.2.23  1993/11/22  21:14:23  meissner
 * 	Add -v option to print the version string.
 * 	[1993/11/22  21:14:16  meissner]
 * 
 * Revision 1.1.2.22  1993/11/22  21:00:50  meissner
 * 	Don't print section name for reloc to defined symbol, print symbol name.
 * 	[1993/11/22  21:00:42  meissner]
 * 
 * Revision 1.1.2.21  1993/11/11  21:40:27  meissner
 * 	Correctly align gprof/prof output.
 * 	Don't print profil ticks in hex.
 * 	[1993/11/11  21:40:12  meissner]
 * 
 * Revision 1.1.2.20  1993/11/11  21:29:19  meissner
 * 	CR 77 - Add profile output support.
 * 	[1993/11/11  21:29:06  meissner]
 * 
 * Revision 1.1.2.19  1993/10/15  18:55:56  meissner
 * 	Fix mmap call to set the MAP_PRIVATE flag.
 * 	[1993/10/15  18:55:44  meissner]
 * 
 * Revision 1.1.2.18  1993/09/23  21:52:22  meissner
 * 	Treat .debug_sfnames as a string section.
 * 	[1993/09/23  21:52:09  meissner]
 * 
 * Revision 1.1.2.17  1993/09/23  21:29:12  meissner
 * 	Skip printing null bytes in string sections, except for the first byte.
 * 	[1993/09/23  21:28:58  meissner]
 * 
 * Revision 1.1.2.16  1993/09/23  21:21:20  meissner
 * 	Print .stabstr section as strings.
 * 	Know about .stab section special format.
 * 	[1993/09/23  21:21:07  meissner]
 * 
 * Revision 1.1.2.15  1993/09/08  22:32:47  meissner
 * 	CR 62: Add -t {d,o,x} to specify output formats.
 * 	[1993/09/08  22:32:35  meissner]
 * 
 * Revision 1.1.2.14  1993/07/27  20:37:07  meissner
 * 	Print 386 relocations in symbolic format.
 * 	[1993/07/27  20:36:52  meissner]
 * 
 * Revision 1.1.2.13  1993/07/23  15:07:30  meissner
 * 	Add definition of STT_STABS if not provided in elf.h.
 * 	Remove ifdefs of STT_STABS elsewhere.
 * 	If -d, invoke objdump -d -j <section> to disassemble executable sections.
 * 	[1993/07/23  15:07:12  meissner]
 * 
 * Revision 1.1.2.12  1993/07/21  01:33:23  meissner
 * 	Print stabs type symbolically, not numerically.
 * 	Rename ELF32_STABS_UNUSED -> ELF32_STABS_OTHER.
 * 	[1993/07/21  01:33:05  meissner]
 * 
 * 	Left align size field, instead of right align.
 * 	[1993/07/18  17:18:12  meissner]
 * 
 * 	Add appropriate casts and/or 'l' specifiers to *printf formats.
 * 	Add STABS in symbol support.
 * 	[1993/07/18  15:07:36  meissner]
 * 
 * Revision 1.1.2.11  1993/06/14  02:15:26  meissner
 * 	Print hash tables again after last change.
 * 	Print interp section in string form, even if -p.
 * 	[1993/06/14  02:15:12  meissner]
 * 
 * Revision 1.1.2.10  1993/06/14  02:02:20  meissner
 * 	CR 52:
 * 	Add -n <name> switch to print a specific section.
 * 	Add -p switch to not print the contents of a progbits section.
 * 	Add -s switch to print header summary only.
 * 	Add -x switch to assume the string table is corrupt.
 * 	[1993/06/14  02:02:04  meissner]
 * 
 * Revision 1.1.2.9  1993/05/27  18:56:26  meissner
 * 	Remove 386 hack which was caused by a buggy compiler.
 * 	[1993/05/27  18:56:11  meissner]
 * 
 * Revision 1.1.2.8  1993/05/24  11:46:12  meissner
 * 	Hack around i386 problem with case SHN_ABS/SHN_COMMON.
 * 	[1993/05/24  11:45:53  meissner]
 * 
 * Revision 1.1.2.7  1993/05/13  17:38:41  meissner
 * 	Add DT_RELAENT.
 * 	[1993/05/13  17:38:24  meissner]
 * 
 * Revision 1.1.2.6  1993/05/12  19:40:17  meissner
 * 	Fix printing of implicit addend in relocation to get full word, not byte.
 * 	Print section being referenced in relocations, symbol tables and hash tables.
 * 	Print name of relocated symbol or section.
 * 	Don't assume REL implicit addend is word aligned.
 * 	Print the dynamic section in a friendly fashion.
 * 	[1993/05/12  19:39:57  meissner]
 * 
 * Revision 1.1.2.5  1993/05/10  21:01:48  meissner
 * 	Fixup so it compiles cleanly on V.4.
 * 	Don't abort if implicit addend too large.
 * 	Fix GCC warnings.
 * 	Relocations for executables have virtual address in r_offset, not section offset.
 * 	Split sym/type in relocations.
 * 	[1993/05/10  21:01:28  meissner]
 * 
 * Revision 1.1.2.4  1993/05/10  15:21:18  meissner
 * 	Print REL implicit addend.
 * 	[1993/05/10  15:20:59  meissner]
 * 
 * Revision 1.1.2.3  1993/05/10  13:38:56  meissner
 * 	Change copyright to OSF_FREE_COPYRIGHT.
 * 	Deal with MAP_VARIABLE not being defined.
 * 	Add hash table support.
 * 	[1993/05/10  13:38:32  meissner]
 * 
 * Revision 1.1.2.2  1993/05/07  19:44:44  meissner
 * 	Initial version.
 * 	[1993/05/07  19:44:20  meissner]
 * 
 * $EndLog$
 */

const char elfdump_what_string[] = "@(#)elfdump.c\t$Revision: 1.1.2.32 $ $Date: 1994/04/08 17:10:34 $ $Locker:  $";

#include <elf.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

#ifdef __OSF1__
#include <sys/time.h>
#include <sys/user.h>
#include <time.h>
#endif

#ifdef NEW_PROFILE
#include <profile/profile.h>

static struct profile_profil profhdr;
static int profhdr_p;
#endif

#ifndef DIGIT_OUTPUT_SIZE
#define DIGIT_OUTPUT_SIZE	64	/* buffer size needed to format integers, includes prefix/suffix */
#endif

extern int getopt (int, char * const [], const char *);
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;

#ifndef MAP_VARIABLE
#define MAP_VARIABLE	MAP_SHARED
#endif

#ifndef EM_MIPS
#define EM_MIPS 8
#endif

#ifndef DT_NULL
/* dynamic structure - page 5-15, figure 5-9 */

typedef struct {
	Elf32_Sword	d_tag;
	union {
	    Elf32_Word	d_val;
	    Elf32_Addr	d_ptr;
	} d_un;
} Elf32_Dyn;

/* Dynamic array tags - page 5-16, figure 5-10.  */

#define DT_NULL		0
#define DT_NEEDED	1
#define DT_PLTRELSZ	2
#define DT_PLTGOT	3
#define DT_HASH		4
#define DT_STRTAB	5
#define DT_SYMTAB	6
#define DT_RELA		7
#define DT_RELASZ	8
#define DT_RELAENT      9
#define DT_STRSZ	10
#define DT_SYMENT	11
#define DT_INIT		12
#define DT_FINI		13
#define DT_SONAME	14
#define DT_RPATH	15
#define DT_SYMBOLIC	16
#define DT_REL		17
#define DT_RELSZ	18
#define DT_RELENT	19
#define DT_PLTREL	20
#define DT_DEBUG	21
#define DT_TEXTREL	22
#define DT_JMPREL	23
#endif

#ifndef STT_STABS

/* GNU extension to encode stabs in elf symbols.  The size field
   is used to encode the unused, type, and desc fields.  */
#define STT_STABS STT_HIPROC

#define ELF32_STABS_OTHER(s)	((s) >> 24)
#define ELF32_STABS_TYPE(s)	(((s) >> 16) & 0xff)
#define ELF32_STABS_DESC(s)	((short) s)
#define ELF32_STABS_SIZE(u,t,d)	(((u) << 24) | (((t) & 0xff) << 16) | ((d) & 0xffff))
#endif

#ifndef ET_PROF
/* OSF extension to encode profiling output as an ELF file.  */

#define ET_PROF 0xffff
#endif

struct sec_list {
  struct sec_list	*next;
  const char		*section;
};

typedef struct elf_stab {
  unsigned int	 soffset;	/* offset to string in .stabstr */
  unsigned char  type;		/* stab type */
  unsigned char  other;		/* stab other field */
  unsigned short desc;		/* stab desc field */
  int		 value;		/* stab value field */
} elf_stab_t;

static char		*file;				/* current file being processed */
static uchar_t		*file_bytes = (uchar_t *)-1;	/* pointer to current object file data */
static struct stat	 file_stat;			/* file status information */
static int		 file_fd	= -1;		/* file descriptor file is openned on */
static Elf32_Ehdr	 ehdr;				/* global header */
static struct sec_list	*sections;			/* sections to print out */
static int		 ignore_string	= 0;		/* ignore the string table */
static int		 objdump	= 0;		/* invoke objdump -d to disassemble text */
static int		 no_progbits	= 0;		/* skip printing progbits section */
static int		 no_layout	= 0;		/* skip printing file/vm layout information */
static int		 summary	= 0;		/* print just summary */
static int		 linker_symbols = 0;		/* put etext/edata/end into layout */
static int		 print_vers	= 0;		/* print version string */
static int		 errors		= 0;		/* # errors found */
static char		 format		= '\0';		/* default format to use */
static int		 sh_name_width	= 0;		/* width of section names */
static int		 sh_type_width  = 0;		/* width of section types */
static int		 sh_start_width	= 0;		/* width of section offset start */
static int		 sh_end_width	= 0;		/* width of section offset end */
static int		 sh_size_width	= 0;		/* width of section size */
static int		 sh_addr_width	= 0;		/* width of section address */
static int		 sh_align_width = 0;		/* width of section alignment */
static int		 sh_flags_width = 0;		/* width of section flag bits */
static int		 sh_index_width = 0;		/* width of section index */
static int		 sh_info_width	= 0;		/* width of section info field */
static int		 sh_link_width	= 0;		/* width of section link field */
static int		 sh_esize_width = 0;		/* width of section entsize field */

#define SEC_PTR(index)	   (((Elf32_Shdr *)(file_bytes + ehdr.e_shoff)) + index)
#define PROGHDR_PTR(index) (((Elf32_Phdr *)(file_bytes + ehdr.e_phoff)) + index)

static const char *const dyn_tag[] = {
  "null",
  "needed",
  "pltrelsz",
  "pltgot",
  "hash",
  "strtab",
  "symtab",
  "rela",
  "relasz",
  "relaent",
  "strsz",
  "syment",
  "init",
  "fini",
  "soname",
  "rpath",
  "symbolic",
  "rel",
  "relsz",
  "relent",
  "pltrel",
  "debug",
  "textrel",
  "jmprel",
};

/* Enum that says to free name in layout chain */
typedef enum layout_free {
  layout_free_no	= 0,	/* do not free name field */
  layout_free_yes	= 1	/* free name field */
} layout_free_t;

/* Structure that records the information about file/virtual layout */
typedef struct layout {
  struct layout *next;		/* next layout element */
  const char *name;		/* element name */
  unsigned long start;		/* start address */
  unsigned long size;		/* size of region */
  layout_free_t free_p;		/* whether to free name or not */
} layout_t;

static layout_t *file_layout	= (layout_t *)0;
static layout_t *vm_layout	= (layout_t *)0;

static const char *get_section_name (Elf32_Word);

\f
/* Allocate memory, abort if no memory is available.  */

static void *
xmalloc (size_t len)
{
  void *p = malloc (len);
  if (!p)
    {
      perror ("malloc");
      exit (1);
    }

  return p;
}

\f
/* Convert a number to a string using a format specified by -t <format> or a default one.  */

#define MAX_STRINGIFY	32	/* if more concurrent calls are made this needs to be bumped */

static const char *
stringify (unsigned long number, int width, int def_format, int skip_lead, const char *prefix, const char *suffix)
{
  static unsigned next_buffer;
  static char def_buffer[MAX_STRINGIFY][DIGIT_OUTPUT_SIZE];

  int use_format = (format) ? format : def_format;
  char *buffer	 = &def_buffer[ (next_buffer++) % MAX_STRINGIFY ][0];

  switch (use_format)
    {
    default:
      sprintf (buffer, "%s%*ld%s", prefix, width, (long) number, suffix);
      break;

    case 'x':
      sprintf (buffer, "%s%s%0*lx%s", prefix, (skip_lead) ? "" : "0x", width, number, suffix);
      break;

    case 'o':
      sprintf (buffer, "%s%s%0*lo%s", prefix, (skip_lead) ? "" : "0", width, number, suffix);
      break;

    case 'u':
      sprintf (buffer, "%s%*lu%s", prefix, width, number, suffix);
      break;
    }

  return buffer;
}

/* Return the length of converting a number to a string via stringify.  */

static int
stringify_len (unsigned long number, int def_format, int skip_lead)
{
  return strlen (stringify (number, 0, def_format, skip_lead, "", ""));
}


/* Silence -Wconversion */
#define stringify(n, w, d, l, p, s) (stringify)((unsigned long)(n), w, d, l, p, s)
#define stringify_len(n, d, l) (stringify_len)((unsigned long)(n), d, l)

\f
/* Add an element to the layout linked list. */

static void
add_layout (layout_t **head_ptr, const char *name, unsigned long start, unsigned long size, layout_free_t free_p)
{
  layout_t *ptr = xmalloc (sizeof (layout_t));

  ptr->next   = *head_ptr;
  ptr->name   = name;
  ptr->start  = start;
  ptr->size   = size;
  ptr->free_p = free_p;
  *head_ptr   = ptr;
}

/* Silence -Wconversion */
#define add_layout(head_ptr, name, start, size, free_p) \
  add_layout (head_ptr, name, (unsigned long)start, (unsigned long)size, free_p)

\f
/*
 * Delete a layout chain
 */

static void
free_layout (layout_t **head_ptr)
{
  layout_t *ptr = *head_ptr;
  layout_t *next;

  *head_ptr = (layout_t *)0;
  while (ptr)
    {
      next = ptr->next;
      if (ptr->free_p)
	free ((void *)ptr->name);

      free ((void *)ptr);
      ptr = next;
    }
}

\f
/* Compare two layout pointers, sorting by start address. */

static int
compare_layout (const void *a, const void *b)
{
  const layout_t *ptr_a = *(const layout_t **)a;
  const layout_t *ptr_b = *(const layout_t **)b;

  return (ptr_a->start != ptr_b->start)
		? ptr_a->start - ptr_b->start
		: ptr_a->size  - ptr_b->size;
}

\f
/* Sort and print a layout linked list */

static void
print_layout (layout_t *head, const char *desc)
{
  size_t num_ptrs;
  size_t i;
  size_t len;
  layout_t *ptr;
  layout_t **ptr_array;
  int width_start = 0;
  int width_end = 0;
  int width_size = 0;
  unsigned long oend;

  if (!head)
    {
      printf ("\nLayout for %s was empty\n", desc);
      return;
    }

  /* Count the number of pointers */
  num_ptrs = 0;
  for (ptr = head; ptr != (layout_t *)0; ptr = ptr->next)
    num_ptrs++;

  /* Sort the layout pointers */
  ptr_array = (layout_t **) xmalloc (num_ptrs * sizeof (layout_t *));
  i = 0;
  for (ptr = head; ptr != (layout_t *)0; ptr = ptr->next)
    ptr_array[i++] = ptr;

  qsort ((void *)ptr_array, num_ptrs, sizeof (layout_t *), compare_layout);

  /* Size start, end */
  for (i = 0; i < num_ptrs; i++)
    {
      ptr = ptr_array[i];
      len = stringify_len (ptr->start, 'x', 1);
      if (len > width_start)
	width_start = len;

      len = stringify_len (ptr->start + ptr->size, 'x', 1);
      if (len > width_end)
	width_end = len;

      len = stringify_len (ptr->size, 'd', 1);
      if (len > width_size)
	width_size = len;
    }

  /* Print out the layout now */
  printf ("\nLayout for %s:\n", desc);
  oend = 0;
  for (i = 0; i < num_ptrs; i++)
    {
      ptr = ptr_array[i];
      if (i > 0 && ptr->start > oend)
	printf ("    start= %s, end= %s, size= %s, name= %s",
		stringify (oend, width_start, 'x', 0, "", ""),
		stringify (ptr->start, width_end, 'x', 0, "", ""),
		stringify (ptr->start - oend, width_size, 'd', 0, "", ""),
		"<hole>\n");

      printf ("    start= %s, end= %s, size= %s, name= %s",
	      stringify (ptr->start, width_start, 'x', 0, "", ""),
	      stringify (ptr->start + ptr->size, width_end, 'x', 0, "", ""),
	      stringify (ptr->size, width_size, 'd', 0, "", ""),
	      ptr->name);

      if (i > 0 && ptr->start < oend)
	printf ("%*s**** Overlap!!", (int)strlen (ptr->name) - 32, "");

      putchar ('\n');
      oend = ptr->start + ptr->size;
    }

  free (ptr_array);
}

\f
/* Convert an elf string reference into a real string.  */

static const char *
elf_string (Elf32_Word sec_index, Elf32_Word byte_off)
{
  static char bad_string[] = "???";

  Elf32_Shdr *sec_hdr = SEC_PTR (sec_index);

  if (ignore_string)
    return bad_string;

  if (sec_index <= 0
      || sec_index > (int)ehdr.e_shnum
      || file_bytes == (uchar_t *)-1
      || (sec_hdr->sh_type != SHT_STRTAB
	  && strcmp (get_section_name (sec_index), ".stabstr") != 0))
    {
      fprintf (stderr, "%s section %s is not a string table.\n", file,
	       stringify (sec_index, 0, 'd', 0, "", ""));

      return bad_string;
    }

  if (byte_off > sec_hdr->sh_size)
    {
      fprintf (stderr, "%s string offset %s too big for section %s\n", file,
	       stringify (byte_off, 0, 'd', 0, "", ""),
	       stringify (sec_index, 0, 'd', 0, "", ""));

      return bad_string;
    }

  return (char *)(file_bytes + sec_hdr->sh_offset + byte_off);
}

\f
/* Convert 1..4 bytes into printable notation.  */

static char *
printable_string (uchar_t *ptr, int num)
{
  static char print_buffer[40];
  int i;
  int ch;
  char *buffer_ptr = print_buffer;

  *buffer_ptr++ = '"';
  for (i = 0; i < num; i++)
    {
      ch = *ptr++;
      switch (ch)
	{
	default:
	  if (isprint (ch))
	    *buffer_ptr++ = ch;

	  else
	    {
	      sprintf (buffer_ptr, "\\%03o", ch);
	      buffer_ptr += 4;
	    }
	  break;

	case '\b': *buffer_ptr++ = '\\'; *buffer_ptr++ = 'b';	break;
	case '\f': *buffer_ptr++ = '\\'; *buffer_ptr++ = 'f';	break;
	case '\n': *buffer_ptr++ = '\\'; *buffer_ptr++ = 'n';	break;
	case '\r': *buffer_ptr++ = '\\'; *buffer_ptr++ = 'r';	break;
	case '\t': *buffer_ptr++ = '\\'; *buffer_ptr++ = 't';	break;
	case '\v': *buffer_ptr++ = '\\'; *buffer_ptr++ = 'v';	break;
	case '\"': *buffer_ptr++ = '\\'; *buffer_ptr++ = '"';	break;
	case '\'': *buffer_ptr++ = '\\'; *buffer_ptr++ = '\'';	break;
	case '\\': *buffer_ptr++ = '\\'; *buffer_ptr++ = '\\';	break;
	}
    }

  *buffer_ptr++ = '"';
  *buffer_ptr   = '\0';

  return print_buffer;
}

\f
/* Convert a stab type into a string.  */

static const char *
printable_stab_type (int type)
{
  switch (type)
    {
      case 0x20: return "GSYM,  ";	/* global symbol: name,,0,type,0 */
      case 0x22: return "FNAME, ";	/* procedure name (f77 kludge): name,,0 */
      case 0x24: return "FUN,   ";	/* procedure: name,,0,linenumber,address */
      case 0x26: return "STSYM, ";	/* static symbol: name,,0,type,address */
      case 0x28: return "LCSYM, ";	/* .lcomm symbol: name,,0,type,address */
      case 0x2a: return "MAIN,  ";	/* Name of main routine, not used in C */
      case 0x30: return "PC,    ";	/* global pascal symbol: name,,0,subtype,line */
      case 0x40: return "RSYM,  ";	/* register sym: name,,0,type,register */
      case 0x44: return "SLINE, ";	/* src line: 0,,0,linenumber,address */
      case 0x46: return "DSLINE,";	/* like N_SLINE, except in data segment */
      case 0x48: return "BROWS, ";	/* source code browser stabs */
      case 0x54: return "CATCH, ";	/* GNU G++ catch clause */
      case 0x60: return "SSYM,  ";	/* structure elt: name,,0,type,struct_offset */
      case 0x64: return "SO,    ";	/* source file name: name,,0,0,address */
      case 0x80: return "LSYM,  ";	/* local sym: name,,0,type,offset */
      case 0x82: return "BINCL, ";	/* aux beginning of include file */
      case 0x84: return "SOL,   ";	/* #included file name: name,,0,0,address */
      case 0xa0: return "PSYM,  ";	/* parameter: name,,0,type,offset */
      case 0xa2: return "EINCL, ";	/* aux end of include file */
      case 0xa4: return "ENTRY, ";	/* alternate entry: name,linenumber,address */
      case 0xc0: return "LBRAC, ";	/* left bracket: 0,,0,nesting level,address */
      case 0xc2: return "EXCL,  ";	/* placeholder for deleted include file */
      case 0xe0: return "RBRAC, ";	/* right bracket: 0,,0,nesting level,address */
      case 0xe2: return "BCOMM, ";	/* begin common: name,, */
      case 0xe4: return "ECOMM, ";	/* end common: name,, */
      case 0xe8: return "ECOML, ";	/* end common (local name): ,,address */
      case 0xfe: return "LENG,  ";	/* second stab entry with length information */
    }

  return stringify (type, 2, 'x', 0, "", ",");
}

\f
/* Return a section's name.  */

static const char *
get_section_name (Elf32_Word sec_index)
{
  Elf32_Shdr *sec_hdr;

  if (sec_index == 0)
    return "<dummy>";

  if (sec_index >= ehdr.e_shnum || ignore_string)
    return stringify (sec_index, 0, 'd', 0, "", "");

  sec_hdr = SEC_PTR (sec_index);
  return elf_string ((Elf32_Word)ehdr.e_shstrndx, sec_hdr->sh_name);
}

\f
/* Print the section body by invoking objdump -d on it.  */

static void
print_section_objdump (Elf32_Shdr *sec_hdr)
{
  pid_t pid;

  fflush ((FILE *)0);

  pid = fork ();
  if (pid == -1)		/* error, no fork */
    perror ("fork");

  else if (pid == 0)		/* child context */
    {
      const char *argv[6];

      argv[0] = "objdump";
      argv[1] = "-d";
      argv[2] = "-j";
      argv[3] = elf_string ((Elf32_Word)ehdr.e_shstrndx, sec_hdr->sh_name);
      argv[4] = file;
      argv[5] = (char *)0;

      execvp ("objdump", (char *const *) argv);
      execv ("/usr/ccs/gcc/objdump", (char *const *) argv);
      execv ("/usr/ccs/gcc-elf/objdump", (char *const *) argv);
      perror ("objdump");
      fflush (stderr);
      _exit (1);
    }

  else				/* parent context */
    {
      int status = 0;
      void (*sigint)(int)  = signal (SIGINT,  SIG_IGN);
      void (*sigquit)(int) = signal (SIGQUIT, SIG_IGN);

      if (waitpid (pid, &status, 0) < 0)
	perror ("waitpid");

      signal (SIGINT,  sigint);
      signal (SIGQUIT, sigquit);

      if (status)
	{
	  if (WIFSIGNALED (status))
	    fprintf (stderr, "objdump exited with signal %s\n",
		     stringify (WTERMSIG (status), 0, 'd', 0, "", ""));
	  else
	    fprintf (stderr, "objdump exited with %s\n",
		     stringify (WEXITSTATUS (status), 0, 'd', 0, "", ""));
	}
    }
}

\f
/* Print the section body just as a series of integers.  */

static void
print_section_raw (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size	   = sec_hdr->sh_size;
  uchar_t	 *ptr	   = file_bytes + sec_hdr->sh_offset;
  int		 sec_off   = 0;
  int		 width_off = stringify_len (size-1, 'd', 1);
  Elf32_Word	 value;

  union { Elf32_Word i; char c[4]; } u;

  if (objdump && (sec_hdr->sh_flags & SHF_EXECINSTR))
    {
      print_section_objdump (sec_hdr);
      return;
    }

  while (size > 0)
    {
      if (size >= 4)
	{
	  if ((((unsigned long)ptr) & ~3u) == 0)
	    value = *(Elf32_Word *)ptr;

	  else
	    {
	      memcpy (u.c, ptr, 4);
	      value = u.i;
	    }

	  printf ("    Offset %s: 0x%.8lx  %11ld  %s\n",
		  stringify (sec_off, width_off, 'd', 0, "", ""),
		  (long)value,
		  (long)value,
		  printable_string (ptr, 4));

	  sec_off += 4;
	  ptr += 4;
	  size -= 4;
	}
      else
	{
	  value = *ptr;
	  printf ("    Offset %s:       0x%.2x  %11d  %s\n",
		  stringify (sec_off, 5, 'd', 0, "", ""),
		  (int)value,
		  (int)value,
		  printable_string (ptr, 1));

	  sec_off++;
	  ptr++;
	  size--;
	}
    }
}

\f
/* Print the special .stab section.  */

static void
print_section_stab (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size		= sec_hdr->sh_size;
  elf_stab_t	*ptr		= (elf_stab_t *)(file_bytes + sec_hdr->sh_offset);
  elf_stab_t	*endptr		= (elf_stab_t *)((char *)ptr + size);
  int		 width_soffset	= 0;
  int		 width_type	= 0;
  int		 width_other	= 0;
  int		 width_desc	= 0;
  int		 width_value	= 0;
  int		 width_stab_num	= stringify_len (endptr - ptr, 'd', 1);
  int		 stab_num	= 0;
  Elf32_Word	 string_sect	= 0;
  int		 len;
  Elf32_Word	 i;

  /* Look for .stabstr section */
  for (i = 0; i < ehdr.e_shnum; i++)
    {
      if (strcmp (get_section_name (i), ".stabstr") == 0)
	{
	  string_sect = i;
	  break;
	}
    }

  /* Find lengths for everything.  */
  while (ptr < endptr)
    {
      len = stringify_len (ptr->soffset, 'd', 1);
      if (len > width_soffset)
	width_soffset = len;

      len = strlen (printable_stab_type (ptr->type));
      if (len > width_type)
	width_type = len;

      len = stringify_len (ptr->desc, 'd', 1);
      if (len > width_desc)
	width_desc = len;

      len = stringify_len (ptr->value, 'd', 1);
      if (len > width_value)
	width_value = len;

      if (ptr->other)
	{
	  len = stringify_len (ptr->other, 'd', 1);
	  if (len > width_other)
	    width_other = len;
	}

      ptr++;
    }

  /* Print fields out.  */
  ptr = (elf_stab_t *)(file_bytes + sec_hdr->sh_offset);
  while (ptr < endptr)
    {
      if (width_other)
	printf ("    Stab #%s: type= %*s other= %s, desc= %s, value= %s, str offset= %s",
		stringify (++stab_num, width_stab_num, 'd', 0, "", ""),
		width_type, printable_stab_type (ptr->type),
		stringify (ptr->other, width_other, 'd', 0, "", ""),
		stringify (ptr->desc, width_desc, 'd', 0, "", ""),
		stringify (ptr->value, width_value, 'd', 0, "", ""),
		stringify (ptr->soffset, width_soffset, 'd', 0, "", ""));
      else
	printf ("    Stab #%s: type= %*s desc= %s, value= %s, str offset= %s",
		stringify (++stab_num, width_stab_num, 'd', 0, "", ""),
		width_type, printable_stab_type (ptr->type),
		stringify (ptr->desc, width_desc, 'd', 0, "", ""),
		stringify (ptr->value, width_value, 'd', 0, "", ""),
		stringify (ptr->soffset, width_soffset, 'd', 0, "", ""));

      if (string_sect)
	printf (", str= %s\n", elf_string (string_sect, ptr->soffset));
      else
	printf ("\n");

      ptr++;
    }
}
  
\f
/* Print the symbol table entries.  */

static void
print_section_symtab (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	  size	 	= sec_hdr->sh_size;
  Elf32_Sym	 *ptr	 	= (Elf32_Sym *)(file_bytes + sec_hdr->sh_offset);
  Elf32_Sym	 *endptr 	= (Elf32_Sym *)((char *)ptr + size);
  int		  sym_num	= 0;
  int		  width_value	= 0;
  int		  width_size	= 0;
  int		  width_shndx	= 0;
  int		  width_sym_num;
  int		  len;
  const char	 *type;
  const char	 *bind;
  const char	 *shndx;
  char		  width_buf[DIGIT_OUTPUT_SIZE + 20];
  char		  size_buf[DIGIT_OUTPUT_SIZE + 20];

  printf ("    String table = \"%s\"\n\n", get_section_name (sec_hdr->sh_link));

  /* Calculate minimum lengths */
  width_sym_num = stringify_len (endptr - ptr, 'd', 1);
  while (ptr < endptr)
    {
      len = stringify_len (ptr->st_value, 'x', 1);
      if (width_value < len)
	width_value = len;

      if (ELF32_ST_TYPE (ptr->st_info) == STT_STABS)
	{
	  len = sprintf (width_buf, "type= %s desc= %s,",
			 printable_stab_type ((int)ELF32_STABS_TYPE (ptr->st_size)),
			 stringify (ELF32_STABS_DESC (ptr->st_size), 0, 'd', 0, "", ""));

	  if (ELF32_STABS_OTHER (ptr->st_size))
	    len += sprintf (width_buf, " other= %s,",
			    stringify (ELF32_STABS_OTHER (ptr->st_size), 0, 'd', 0, "", ""));
	}
      else
	len = sprintf (width_buf, "size= %s,",
		       stringify (ptr->st_size, 0, 'd', 0, "", ""));

      if (width_size < len)
	width_size = len;

      switch (ptr->st_shndx)
	{
	default:
	  len = (ignore_string)
			? stringify_len (ptr->st_shndx, 'd', 0)
			: strlen (get_section_name (ptr->st_shndx));
	  break;

	case SHN_UNDEF:  len = sizeof ("undef")-1;	break;
	case SHN_ABS:    len = sizeof ("abs")-1;	break;
	case SHN_COMMON: len = sizeof ("common")-1;	break;
	}

      if (width_shndx < len)
	width_shndx = len;

      ptr++;
    }

  ptr = (Elf32_Sym *)(file_bytes + sec_hdr->sh_offset);
  while (ptr < endptr)
    {
      switch (ELF32_ST_BIND (ptr->st_info))
	{
	default:
	  bind = stringify (ELF32_ST_BIND (ptr->st_info), 0, 'd', 0, "", ",");
	  break;

	case STB_LOCAL:  bind = "local,";	break;
	case STB_GLOBAL: bind = "global,";	break;
	case STB_WEAK:   bind = "weak,"; 	break;
	}

      switch (ELF32_ST_TYPE (ptr->st_info))
	{
	default:
	  type = stringify (ELF32_ST_TYPE (ptr->st_info), 0, 'd', 0, "", ",");
	  break;

	case STT_NOTYPE:	type = "none,";		break;
	case STT_OBJECT:	type = "obj,";		break;
	case STT_FUNC:		type = "func,";		break;
	case STT_SECTION:	type = "sect,";		break;
	case STT_FILE:		type = "file,";		break;
	case STT_STABS:		type = "stab,";		break;
	}

      switch ((int)ptr->st_shndx)
	{
	default:
	  shndx = (ignore_string)
			? stringify (ptr->st_shndx, 0, 'd', 0, "", "")
			: get_section_name (ptr->st_shndx);
	  break;

	case (int)SHN_UNDEF:	shndx = "undef";	break;
	case (int)SHN_ABS:	shndx = "abs";		break;
	case (int)SHN_COMMON:	shndx = "common";	break;
	}

      if (ELF32_ST_TYPE (ptr->st_info) == STT_STABS)
	{
	  len = sprintf (size_buf, "type= %s desc= %s,",
			 printable_stab_type ((int)ELF32_STABS_TYPE (ptr->st_size)),
			 stringify (ELF32_STABS_DESC (ptr->st_size), 0, 'd', 0, "", ""));

	  if (ELF32_STABS_OTHER (ptr->st_size))
	    sprintf (size_buf + len, " other= %s,",
		     stringify (ELF32_STABS_OTHER (ptr->st_size), 0, 'd', 0, "", ""));
	}
      else
	sprintf (size_buf, "size= %s,", stringify (ptr->st_size, 0, 'd', 0, "", ""));

      printf ("    Sym %s: value= %s, %-*s sect= %s%-*s type= %-5s bind= %-7s name= %s\n",
	      stringify (sym_num, width_sym_num, 'd', 0, "", ""),
	      stringify (ptr->st_value, width_value, 'x', 0, "", ""),
	      width_size, size_buf,
	      shndx, width_shndx + 1 - (int)strlen (shndx), ",",
	      type, bind, elf_string (sec_hdr->sh_link, ptr->st_name));

      ptr++;
      sym_num++;
    }
}

\f
/* Print the hash table.  */

static void
print_section_hash (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size	 = sec_hdr->sh_size;
  Elf32_Word	*ptr	 = (Elf32_Word *)(file_bytes + sec_hdr->sh_offset);
  Elf32_Word	*endptr	 = (Elf32_Word *)((char *)ptr + size);
  Elf32_Word	 nbucket = *ptr++;
  Elf32_Word	 nchain  = *ptr++;
  Elf32_Word	*hchain  = ptr + nbucket;
  int		 loading;
  Elf32_Word	 i;
  Elf32_Word	 element;
  Elf32_Word	 next;
  int		 width;


  if (endptr != (ptr + nbucket + nchain))
    {
      printf ("    <hash table wrong size>\n");
      print_section_raw (sec_hdr);
      return;
    }

  width = stringify_len ((nchain > nbucket) ? nchain : nbucket, 'd', 1);
  printf ("    Symbol table = \"%s\"\n", get_section_name (sec_hdr->sh_link));
  printf ("    # Buckets    = %s\n", stringify (nbucket, width, 'd', 0, "", ""));
  printf ("    # Elements   = %s\n\n", stringify (nchain, width, 'd', 0, "", ""));

  for (i = 0; i < nbucket; i++)
    {
      loading = 1;
      element = *ptr;
      while ((next = hchain[element]) != 0)
	{
	  loading++;
	  element = next;
	}

      printf ("    Bucket %s: first: %s, loading: %s\n",
	      stringify (i, width, 'd', 0, "", ""),
	      stringify (*ptr++, width, 'd', 0, "", ""),
	      stringify (loading, width, 'd', 0, "", ""));
    }

  putchar ('\n');
  for (i = 0; i < nchain; i++)
    printf ("    Chain  %s: %s\n",
	    stringify (i, width, 'd', 0, "", ""),
	    stringify (*ptr++, width, 'd', 0, "", ""));
}

\f
/* Print the string section body.  */

static void
print_section_string (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	  size	    = sec_hdr->sh_size;
  char		 *ptr	    = (char *)(file_bytes + sec_hdr->sh_offset);
  char		 *endptr    = ptr + size;
  int		  sec_off   = 0;
  int		  len	    = 0;
  int		  width_off = stringify_len (size-1, 'd', 1);

  while (ptr < endptr)
    {
      if (*ptr != '\0' || sec_off == 0)
	printf ("    Offset %s: \"%s\"\n", stringify (sec_off, width_off, 'd', 0, "", ""), ptr);

      len = strlen (ptr) + 1;
      ptr += len;
      sec_off += len;
    }
}

\f
/* Decode a relocation type into a printable value.  */

static const char *
get_reloc_type (int type)
{
  static const char *reloc_386[] =
    {
      "R_386_NONE,",
      "R_386_32,",
      "R_386_PC32,",
      "R_386_GOT,",
      "R_386_PLT32,",
      "R_386_COPY,",
      "R_386_GLOB_DAT,",
      "R_386_JMP_SLOT,",
      "R_386_RELATIVE,",
      "R_386_GOTOFF,",
      "R_386_GOTPC,",
    };

  if (ehdr.e_machine == EM_386 && (unsigned)type < (sizeof (reloc_386) / sizeof (char *)))
    return reloc_386[type];

  return stringify (type, 0, 'd', 0, "", ",");
}

\f
/* Print RELA relocations.  */

static void
print_section_rela (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size		= sec_hdr->sh_size;
  Elf32_Rela	*ptr		= (Elf32_Rela *)(file_bytes + sec_hdr->sh_offset);
  Elf32_Rela	*endptr		= (Elf32_Rela *)((char *)ptr + size);
  Elf32_Shdr	*sym_sec	= SEC_PTR (sec_hdr->sh_link);
  Elf32_Sym	*sym_ptr	= (Elf32_Sym *)(file_bytes + sym_sec->sh_offset);
  int		 rel_object_p	= (ehdr.e_type == ET_REL);
  int		 rel_num	= 0;
  int		 width_offset	= 0;
  int		 width_info	= 0;
  int		 width_sym	= 0;
  int		 width_type	= 0;
  int		 width_addend	= 0;
  int		 width_rel_num;
  int		 sym_num;
  int		 len;
  Elf32_Word	 offset;
  const char	*sect_or_undef;
  const char	*name;
  const char	*offset_value;

  printf ("    Symbol table section    = \"%s\"\n",   get_section_name (sec_hdr->sh_link));
  printf ("    Section being relocated = \"%s\"\n\n", get_section_name (sec_hdr->sh_info));

  /* Calculate minimum lengths */
  width_rel_num = stringify_len (endptr - ptr, 'd', 1);
  while (ptr < endptr)
    {
      if (rel_object_p)
	offset = ptr->r_offset;
      else
	offset = ptr->r_offset - sec_hdr->sh_addr;

      len = stringify_len (offset, 'd', 1);
      if (width_offset < len)
	width_offset = len;

      len = stringify_len (ptr->r_info, 'd', 1);
      if (width_info < len)
	width_info = len;

      len = stringify_len (ELF32_R_SYM (ptr->r_info), 'd', 1);
      if (width_sym < len)
	width_sym = len;

      len = strlen (get_reloc_type (ELF32_R_TYPE (ptr->r_info)));
      if (width_type < len)
	width_type = len;

      len = stringify_len (ptr->r_addend, 'd', 0);
      if (width_addend < len)
	width_addend = len;

      ptr++;
    }

  ptr = (Elf32_Rela *)(file_bytes + sec_hdr->sh_offset);
  while (ptr < endptr)
    {
      if (rel_object_p)
	{
	  offset = ptr->r_offset;
	  offset_value = "";
	}
      else
	{
	  offset = ptr->r_offset - sec_hdr->sh_addr;
	  offset_value = stringify (ptr->r_offset, 8, 'x', 0, " [", "]");
	}

      sym_num = ELF32_R_SYM (ptr->r_info);
      switch ((int)sym_ptr[sym_num].st_shndx)
	{
	default:		sect_or_undef = "internal";	break;
	case (int)SHN_UNDEF:	sect_or_undef = "external";	break;
	case (int)SHN_COMMON:	sect_or_undef = "common  ";	break;
	case (int)SHN_ABS:	sect_or_undef = "absolute";	break;
	}

      name = elf_string (sym_sec->sh_link, sym_ptr[sym_num].st_name);
      printf ("    Reloc %s: offset= %s%s, info= %s, sym= %s, type= %-*s addend= %s, %s \"%s\"\n",
	      stringify (rel_num, width_rel_num, 'd', 0, "", ""),
	      stringify (offset, width_offset, 'd', 0, "", ""), offset_value,
	      stringify (ptr->r_info, width_info, 'd', 0, "", ""),
	      stringify (sym_num, width_sym, 'd', 0, "", ""),
	      width_type, get_reloc_type (ELF32_R_TYPE (ptr->r_info)),
	      stringify (ptr->r_addend, width_addend, 'd', 0, "", ""),
	      sect_or_undef, name);

      ptr++;
      rel_num++;
    }
}

\f
/* Print REL relocations.  */

static void
print_section_rel (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size		= sec_hdr->sh_size;
  Elf32_Rel	*ptr		= (Elf32_Rel *)(file_bytes + sec_hdr->sh_offset);
  Elf32_Rel	*endptr		= (Elf32_Rel *)((char *)ptr + size);
  Elf32_Shdr	*sym_sec	= SEC_PTR (sec_hdr->sh_link);
  Elf32_Sym	*sym_ptr	= (Elf32_Sym *)(file_bytes + sym_sec->sh_offset);
  Elf32_Shdr	*data_sec	= SEC_PTR (sec_hdr->sh_info);
  char		*data_ptr	= (char *)(file_bytes + data_sec->sh_offset);
  int		 rel_object_p	= (ehdr.e_type == ET_REL);
  int		 rel_num	= 0;
  int		 width_offset	= 0;
  int		 width_info	= 0;
  int		 width_sym	= 0;
  int		 width_type	= 0;
  int		 width_addend	= 0;
  int		 width_rel_num;
  int		 sym_num;
  int		 len;
  Elf32_Word	 offset;
  const char	*sect_or_undef;
  const char	*name;
  const char	*offset_value;
  const char	*addend_value;

  union {
    Elf32_Word	i;
    char	c[sizeof (Elf32_Word)];
  } addend;

  printf ("    Symbol table section    = \"%s\"\n",   get_section_name (sec_hdr->sh_link));
  printf ("    Section being relocated = \"%s\"\n\n", get_section_name (sec_hdr->sh_info));

  /* Calculate minimum lengths */
  width_rel_num = stringify_len (endptr - ptr, 'd', 1);
  while (ptr < endptr)
    {
      if (rel_object_p)
	offset = ptr->r_offset;
      else
	offset = ptr->r_offset - sec_hdr->sh_addr;

      len = stringify_len (offset, 'd', 1);
      if (width_offset < len)
	width_offset = len;

      len = stringify_len (ptr->r_info, 'd', 1);
      if (width_info < len)
	width_info = len;

      len = stringify_len (ELF32_R_SYM (ptr->r_info), 'd', 1);
      if (width_sym < len)
	width_sym = len;

      len = strlen (get_reloc_type (ELF32_R_TYPE (ptr->r_info)));
      if (width_type < len)
	width_type = len;

      if (offset < data_sec->sh_size)
	{
	  memcpy (addend.c, data_ptr + offset, sizeof (Elf32_Word));
	  len = stringify_len (addend.i, 'd', 0);
	}
      else
	len = sizeof ("error")-1;

      if (width_addend < len)
	width_addend = len;

      ptr++;
    }

  ptr = (Elf32_Rel *)(file_bytes + sec_hdr->sh_offset);
  while (ptr < endptr)
    {
      if (rel_object_p)
	{
	  offset = ptr->r_offset;
	  offset_value = "";
	}
      else
	{
	  offset = ptr->r_offset - sec_hdr->sh_addr;
	  offset_value = stringify (ptr->r_offset, 8, 'x', 0, " [", "]");
	}

      if (offset < data_sec->sh_size)
	{
	  memcpy (addend.c, data_ptr + offset, sizeof (Elf32_Word));
	  addend_value = stringify (addend.i, 0, 'd', 0, "", "");
	}
      else
	addend_value = "error";

      sym_num = ELF32_R_SYM (ptr->r_info);
      switch ((int)sym_ptr[sym_num].st_shndx)
	{
	default:		sect_or_undef = "internal";	break;
	case (int)SHN_UNDEF:	sect_or_undef = "external";	break;
	case (int)SHN_COMMON:	sect_or_undef = "common  ";	break;
	case (int)SHN_ABS:	sect_or_undef = "absolute";	break;
	}

      name = elf_string (sym_sec->sh_link, sym_ptr[sym_num].st_name);
      printf ("    Reloc %s: offset= %s%s, info= %s, sym= %s, type= %-*s addend= %*s, %s \"%s\"\n",
	      stringify (rel_num, width_rel_num, 'd', 0, "", ""),
	      stringify (offset, width_offset, 'd', 0, "", ""), offset_value,
	      stringify (ptr->r_info, width_info, 'd', 0, "", ""),
	      stringify (sym_num, width_sym, 'd', 0, "", ""),
	      width_type, get_reloc_type (ELF32_R_TYPE (ptr->r_info)),
	      width_addend, addend_value,
	      sect_or_undef, name);

      ptr++;
      rel_num++;
    }
}

\f
/* Print the dynamic link structures.  */

static void
print_section_dynamic (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size		= sec_hdr->sh_size;
  Elf32_Dyn	*ptr		= (Elf32_Dyn *)(file_bytes + sec_hdr->sh_offset);
  Elf32_Dyn	*endptr		= (Elf32_Dyn *)((char *)ptr + size);
  int		 dyn_num	= 0;
  int		 width_tag_str	= 0;
  int		 width_val_num	= 0;
  int		 width_dyn_num;
  int		 len;
  const char	*tag;

  /* Calculate minimum lengths */
  width_dyn_num = stringify_len (endptr - ptr, 'd', 1);
  while (ptr < endptr)
    {
      if (ptr->d_tag >= 0 && ptr->d_tag < sizeof (dyn_tag) / sizeof (dyn_tag[0]))
	len = strlen (dyn_tag[ ptr->d_tag ]);
      else
	len = stringify_len (ptr->d_tag, 'd', 1);

      if (width_tag_str < len)
	width_tag_str = len;

      len = stringify_len (ptr->d_un.d_val, 'd', 1);
      if (width_val_num < len)
	width_val_num = len;

      ptr++;
    }

  ptr = (Elf32_Dyn *)(file_bytes + sec_hdr->sh_offset);
  while (ptr < endptr)
    {
      if (ptr->d_tag >= 0 && ptr->d_tag < sizeof (dyn_tag) / sizeof (dyn_tag[0]))
	tag = dyn_tag[ ptr->d_tag ];
      else
	tag = stringify (ptr->d_tag, 0, 'd', 0, "", "");

      printf ("    Dynamic record %s: tag= %*s, value= 0x%.8lx, %*ld\n",
	      stringify (dyn_num, width_dyn_num, 'd', 0, "", ""),
	      width_tag_str, tag,
	      (long)ptr->d_un.d_val, width_val_num, (long)ptr->d_un.d_val);

      ptr++;
      dyn_num++;
    }
}

\f
/* Print the profil section.  */

static void
print_section_profil (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size	   = sec_hdr->sh_size;
  uchar_t	*ptr	   = file_bytes + sec_hdr->sh_offset;
  int		 sec_off   = 0;
  int		 width_off = stringify_len (size, 'd', 1);
  int		 entsize   = sec_hdr->sh_entsize;
  Elf32_Word	 value;

  printf ("    Low  pc = %s\n",   stringify (sec_hdr->sh_link, width_off, 'x', 0, "", ""));
  printf ("    High pc = %s\n\n", stringify (sec_hdr->sh_info, width_off, 'x', 0, "", ""));

  if ((size % entsize) != 0)
    {
      print_section_raw (sec_hdr);
      return;
    }

  if (entsize != sizeof (short)
      && entsize != sizeof (unsigned)
#ifdef NEW_PROFILE
      && entsize != sizeof (LHISTCOUNTER)
#endif
      && entsize != sizeof (long unsigned))
    {
      print_section_raw (sec_hdr);
      return;
    }

  while (size > 0)
    {
      if (entsize == sizeof (short))
	{
	  if ((((unsigned long)ptr) & ~(sizeof(short) - 1)) == 0)
	    value = *(short *)ptr;

	  else
	    {
	      union { short i; char c[sizeof (short)]; } u;
	      memcpy (u.c, ptr, sizeof (short));
	      value = u.i;
	    }

	  if (value)
	    printf ("    Offset %s: %s ticks\n",
		    stringify (sec_off, width_off, 'd', 0, "", ""),
		    stringify (value, 6, 'u', 0, "", ""));
	}

      else if (entsize == sizeof (unsigned))
	{
	  if ((((unsigned long)ptr) & ~(sizeof(unsigned) - 1)) == 0)
	    value = *(unsigned *)ptr;

	  else
	    {
	      union { unsigned i; char c[sizeof (unsigned)]; } u;
	      memcpy (u.c, ptr, sizeof (unsigned));
	      value = u.i;
	    }

	  if (value)
	    printf ("    Offset %s: %s ticks\n",
		    stringify (sec_off, width_off, 'd', 0, "", ""),
		    stringify (value, 11, 'u', 0, "", ""));
	}

      else if (entsize == sizeof (long unsigned))
	{
	  if ((((unsigned long)ptr) & ~(sizeof(long unsigned) - 1)) == 0)
	    value = *(long unsigned *)ptr;

	  else
	    {
	      union { long unsigned i; char c[sizeof (long unsigned)]; } u;
	      memcpy (u.c, ptr, sizeof (long unsigned));
	      value = u.i;
	    }

	  if (value)
	    printf ("    Offset %s: %s tick(s)\n",
		    stringify (sec_off, width_off, 'd', 0, "", ""),
		    stringify (value, 11, 'd', 0, "", ""));
	}

#ifdef NEW_PROFILE
      else if (entsize == sizeof (LHISTCOUNTER))
	{
	  long double dvalue;
	  if ((((unsigned long)ptr) & ~(sizeof(LHISTCOUNTER) - 1)) == 0)
	    dvalue = LPROF_CNT_TO_LDOUBLE (*(LHISTCOUNTER *)ptr);

	  else
	    {
	      union { LHISTCOUNTER i; char c[sizeof (LHISTCOUNTER)]; } u;
	      memcpy (u.c, ptr, sizeof (LHISTCOUNTER));
	      dvalue = LPROF_CNT_TO_LDOUBLE (u.i);
	    }

	  if (dvalue)
	    printf ("    Offset %s: %16.0Lf tick(s)\n",
		    stringify (sec_off, width_off, 'd', 0, "", ""),
		    dvalue);
	}
#endif

      else
	abort ();

      sec_off += entsize;
      ptr += entsize;
      size -= entsize;
    }
}

\f
/* Print the gprof section.  */

static void
print_section_gprof (Elf32_Shdr *sec_hdr)
{
  struct gprof_arc {
    long frompc;
    long selfpc;
    long count;
  } arc;

  Elf32_Word	 size	    = sec_hdr->sh_size;
  uchar_t	*ptr	    = file_bytes + sec_hdr->sh_offset;
  int		 arc_num    = 1;
  int		 width_arc  = stringify_len ((size / sizeof (arc)), 'd', 1);
  int		 width_from = 0;
  int		 width_self = 0;
  int		 width_cnt  = 0;
  int		 len;

  if (sec_hdr->sh_entsize != sizeof (arc) || (size % sizeof (arc)) != 0)
    {
      print_section_raw (sec_hdr);
      return;
    }

  while (size > 0)
    {
      memcpy ((void *)&arc, ptr, sizeof (arc));
      len = stringify_len (arc.frompc, 'x', 1);
      if (len > width_from)
	width_from = len;

      len = stringify_len (arc.selfpc, 'x', 1);
      if (len > width_self)
	width_self = len;

      len = stringify_len (arc.count, 'd', 1);
      if (len > width_cnt)
	width_cnt = len;

      ptr  += sizeof (arc);
      size -= sizeof (arc);
    }

  size = sec_hdr->sh_size;
  ptr  = file_bytes + sec_hdr->sh_offset;
  while (size > 0)
    {
      memcpy ((void *)&arc, ptr, sizeof (arc));
      printf ("    Arc %s: Frompc= %s, Selfpc= %s, Count= %s\n",
	      stringify (arc_num++,  width_arc,  'd', 0, "", ""),
	      stringify (arc.frompc, width_from, 'x', 0, "", ""),
	      stringify (arc.selfpc, width_self, 'x', 0, "", ""),
	      stringify (arc.count,  width_cnt,  'd', 0, "", ""));

      ptr  += sizeof (arc);
      size -= sizeof (arc);
    }
}

\f
/* Print the prof section.  */

static void
print_section_prof (Elf32_Shdr *sec_hdr)
{
  struct prof_arc {
    long selfpc;
    long count;
  } record;

  Elf32_Word	 size	    = sec_hdr->sh_size;
  uchar_t	*ptr	    = file_bytes + sec_hdr->sh_offset;
  int		 rec_num    = 1;
  int		 width_rec  = stringify_len ((size / sizeof (record)), 'd', 1);
  int		 width_self = 0;
  int		 width_cnt  = 0;
  int		 len;

  if (sec_hdr->sh_entsize != sizeof (record) || (size % sizeof (record)) != 0)
    {
      print_section_raw (sec_hdr);
      return;
    }

  while (size > 0)
    {
      memcpy ((void *)&record, ptr, sizeof (record));
      len = stringify_len (record.selfpc, 'x', 1);
      if (len > width_self)
	width_self = len;

      len = stringify_len (record.count, 'd', 1);
      if (len > width_cnt)
	width_cnt = len;

      ptr  += sizeof (record);
      size -= sizeof (record);
    }

  size = sec_hdr->sh_size;
  ptr  = file_bytes + sec_hdr->sh_offset;
  while (size > 0)
    {
      memcpy ((void *)&record, ptr, sizeof (record));
      printf ("    Record %s: Selfpc= %s, Count= %s\n",
	      stringify (rec_num++,     width_rec,  'd', 0, "", ""),
	      stringify (record.selfpc, width_self, 'x', 0, "", ""),
	      stringify (record.count,  width_cnt,  'd', 0, "", ""));

      ptr  += sizeof (record);
      size -= sizeof (record);
    }
}

\f
#ifdef __OSF1__

/* Print the uarea section in the profiling output.  */

static void
print_section_uarea (Elf32_Shdr *sec_hdr)
{
  Elf32_Word	 size	    = sec_hdr->sh_size;
  struct user	*ptr	    = (struct user *)(file_bytes + sec_hdr->sh_offset);

  print_section_raw (sec_hdr);

  if (size == sizeof (struct user))
    {
      int width = 0;
      int len;
      time_t t = ptr->u_start.tv_sec;
      double u_time = ptr->u_ru.ru_utime.tv_sec + (ptr->u_ru.ru_utime.tv_usec / 1000000.0);
      double s_time = ptr->u_ru.ru_stime.tv_sec + (ptr->u_ru.ru_stime.tv_usec / 1000000.0);
      char buf[30];

      len = sprintf (buf, "%.4f", u_time);
      if (len > width)
	width = len;

      len = sprintf (buf, "%.4f", s_time);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_maxrss, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_ixrss, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_idrss, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_isrss, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_minflt, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_majflt, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_nswap, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_inblock, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_oublock, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_msgsnd, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_msgrcv, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_nsignals, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_nvcsw, 'd', 1);
      if (len > width)
	width = len;

      len = stringify_len (ptr->u_ru.ru_nivcsw, 'd', 1);
      if (len > width)
	width = len;

      putchar ('\n');
      printf ("    u_comm           = %s\n", ptr->u_comm);
      printf ("    u_logname        = %s\n", ptr->u_logname);
      printf ("    u_start          = %s", ctime (&t));
      printf ("    u_ru.ru_utime    = %*.4f seconds\n", width, u_time);
      printf ("    u_ru.ru_stime    = %*.4f seconds\n", width, s_time);

      printf ("    u_ru.ru_maxrss   = %s [%s]\n", stringify (ptr->u_ru.ru_maxrss, width, 'd', 0, "", ""),
	      "max memory size");

      printf ("    u_ru.ru_ixrss    = %s [%s]\n", stringify (ptr->u_ru.ru_ixrss, width, 'd', 0, "", ""),
	      "integral shared memory size");

      printf ("    u_ru.ru_idrss    = %s [%s]\n", stringify (ptr->u_ru.ru_idrss, width, 'd', 0, "", ""),
	      "integral unshared data");

      printf ("    u_ru.ru_isrss    = %s [%s]\n", stringify (ptr->u_ru.ru_isrss, width, 'd', 0, "", ""),
	      "integral unshared stack");

      printf ("    u_ru.ru_minflt   = %s [%s]\n", stringify (ptr->u_ru.ru_minflt, width, 'd', 0, "", ""),
	      "page reclaims");

      printf ("    u_ru.ru_majflt   = %s [%s]\n", stringify (ptr->u_ru.ru_majflt, width, 'd', 0, "", ""),
	      "page faults");

      printf ("    u_ru.ru_nswap    = %s [%s]\n", stringify (ptr->u_ru.ru_nswap, width, 'd', 0, "", ""),
	      "swaps");

      printf ("    u_ru.ru_inblock  = %s [%s]\n", stringify (ptr->u_ru.ru_inblock, width, 'd', 0, "", ""),
	      "block input operations");

      printf ("    u_ru.ru_oublock  = %s [%s]\n", stringify (ptr->u_ru.ru_oublock, width, 'd', 0, "", ""),
	      "block output operations");

      printf ("    u_ru.ru_msgsnd   = %s [%s]\n", stringify (ptr->u_ru.ru_msgsnd, width, 'd', 0, "", ""),
	      "messages sent");

      printf ("    u_ru.ru_msgrcv   = %s [%s]\n", stringify (ptr->u_ru.ru_msgrcv, width, 'd', 0, "", ""),
	      "messages received");

      printf ("    u_ru.ru_nsignals = %s [%s]\n", stringify (ptr->u_ru.ru_nsignals, width, 'd', 0, "", ""),
	      "signals received");

      printf ("    u_ru.ru_nvcsw    = %s [%s]\n", stringify (ptr->u_ru.ru_nvcsw, width, 'd', 0, "", ""),
	      "voluntary context switches");

      printf ("    u_ru.ru_nivcsw   = %s [%s]\n", stringify (ptr->u_ru.ru_nivcsw, width, 'd', 0, "", ""),
	      "involuntary context switches");
    }
}

#endif
\f
#ifdef NEW_PROFILE
/* Print the stats section.  */

static void
print_section_stats (Elf32_Shdr *sec_hdr)
{
  struct profile_stats *ptr = (struct profile_stats *)(file_bytes + sec_hdr->sh_offset);

  if (sec_hdr->sh_size != sizeof (struct profile_stats))
    print_section_raw (sec_hdr);
  else
    _profile_print_stats(stdout,
			 ptr,
			 (profhdr_p) ? &profhdr : (struct profile_profil *)0);
}
#endif

\f
#ifdef NEW_PROFILE
/* Print the profil header section.  */

static void
print_section_profhdr (Elf32_Shdr *sec_hdr)
{
  struct profile_profil *ptr = (struct profile_profil *)(file_bytes + sec_hdr->sh_offset);

  if (sec_hdr->sh_size == sizeof (struct profile_profil))
    {
      profhdr = *ptr;
      profhdr_p = 1;
    }

  print_section_raw (sec_hdr);
}
#endif

\f
/* Print the progbits section.  */

static void
print_section_progbits (Elf32_Shdr *sec_hdr)
{
  const char *name = elf_string ((Elf32_Word)ehdr.e_shstrndx, sec_hdr->sh_name);

  if (strcmp (name, ".interp") == 0)
    print_section_string (sec_hdr);

  else if (no_progbits)
    printf ("    Section body skipped...\n");

  else if (strcmp (name, ".comment") == 0)
    print_section_string (sec_hdr);

  else if (strcmp (name, ".stab") == 0)
    print_section_stab (sec_hdr);

  else if (strcmp (name, ".stabstr") == 0)
    print_section_string (sec_hdr);

  else if (strcmp (name, ".debug_sfnames") == 0)
    print_section_string (sec_hdr);


  else if (sec_hdr->sh_type == SHT_PROGBITS && ehdr.e_type == ET_PROF)
    {
      if (strcmp (name, ".profil") == 0 || strcmp (name, ".lprofil") == 0)
	print_section_profil (sec_hdr);

      else if (strcmp (name, ".gprof") == 0)
	print_section_gprof (sec_hdr);

      else if (strcmp (name, ".prof") == 0)
	print_section_prof (sec_hdr);

#ifdef __OSF1__
      else if (strcmp (name, ".uarea") == 0)
	print_section_uarea (sec_hdr);
#endif

#ifdef NEW_PROFILE
      else if (strcmp (name, ".stats") == 0)
	print_section_stats (sec_hdr);

      else if (strcmp (name, ".profhdr") == 0)
	print_section_profhdr (sec_hdr);

      else
	print_section_raw (sec_hdr);
#endif
    }

  else
    print_section_raw (sec_hdr);
}

\f
/* Size the max fields in printing section headers. */

static void
size_section (Elf32_Word sec_index)
{
  Elf32_Shdr	  *sec_hdr	= SEC_PTR (sec_index);
  const char	  *name;
  const char	  *sec_type;
  struct sec_list *sec_ptr;
  int		   len;

  name = get_section_name (sec_index);
  if (sec_index)		/* skip dummy section */
    {
      add_layout (&file_layout, name, sec_hdr->sh_offset,
		  ((int)sec_hdr->sh_type == SHT_NOBITS) ? 0 : sec_hdr->sh_size, layout_free_no);

      if (ehdr.e_phnum && (sec_hdr->sh_flags & SHF_ALLOC))
	add_layout (&vm_layout, name, sec_hdr->sh_addr, sec_hdr->sh_size, layout_free_no);
    }

  if (!summary)
    return;

  /* Skip section if desired.  */
  if (sections)
    {
      for (sec_ptr = sections; sec_ptr != (struct sec_list *)0; sec_ptr = sec_ptr->next)
	{
	  if (strcmp (name, sec_ptr->section) == 0)
	    break;
	}

      if (!sec_ptr)
	return;
    }

  len = stringify_len (sec_index, 'd', 1);
  if (len > sh_index_width)
    sh_index_width = len;

  len = strlen (name);
  if (len > sh_name_width)
    sh_name_width = len;

  switch ((int)sec_hdr->sh_type)
    {
    default:
      sec_type = stringify (sec_hdr->sh_type, 0, 'd', 0, "type=", "");
      break;

    case SHT_NULL:	sec_type = "none";	break;
    case SHT_PROGBITS:	sec_type = "progbits";	break;
    case SHT_SYMTAB:	sec_type = "symtab";	break;
    case SHT_STRTAB:	sec_type = "strtab";	break;
    case SHT_RELA:	sec_type = "rela";	break;
    case SHT_HASH:	sec_type = "hash";	break;
    case SHT_DYNAMIC:	sec_type = "dynamic";	break;
    case SHT_NOTE:	sec_type = "note";	break;
    case SHT_NOBITS:	sec_type = "nobits";	break;
    case SHT_REL:	sec_type = "rel";	break;
    case SHT_SHLIB:	sec_type = "shlib";	break;
    case SHT_DYNSYM:	sec_type = "dynsym";	break;
    }

  len = strlen (sec_type);
  if (len > sh_type_width)
    sh_type_width = len;

  len = stringify_len (sec_hdr->sh_offset, 'd', 1);
  if (len > sh_start_width)
    sh_start_width = len;

  len = stringify_len (sec_hdr->sh_size, 'd', 1);
  if (len > sh_size_width)
    sh_size_width = len;

  len = stringify_len (sec_hdr->sh_offset + (((int)sec_hdr->sh_type == SHT_NOBITS) ? 0 : sec_hdr->sh_size), 'x', 1);
  if (len > sh_end_width)
    sh_end_width = len;

  len = stringify_len (sec_hdr->sh_addr, 'x', 1);
  if (len > sh_addr_width)
    sh_addr_width = len;

  len = stringify_len (sec_hdr->sh_addralign, 'd', 1);
  if (len > sh_align_width)
    sh_align_width = len;

  len = stringify_len (sec_hdr->sh_flags, 'x', 1);
  if (len > sh_flags_width)
    sh_flags_width = len;

  len = stringify_len (sec_hdr->sh_info, 'd', 1);
  if (len > sh_info_width)
    sh_info_width = len;

  len = stringify_len (sec_hdr->sh_link, 'd', 1);
  if (len > sh_link_width)
    sh_link_width = len;

  len = stringify_len (sec_hdr->sh_entsize, 'd', 1);
  if (len > sh_esize_width)
    sh_esize_width = len;
}

\f
/* Print a section header. */

static void
print_section (Elf32_Word sec_index)
{
  Elf32_Shdr	  *sec_hdr	= SEC_PTR (sec_index);
  const char	  *sec_type;
  const char	  *name;
  struct sec_list *sec_ptr;
  void (*sec_func)(Elf32_Shdr *);

  /* Skip section if desired.  */
  name = get_section_name (sec_index);
  if (sections)
    {
      for (sec_ptr = sections; sec_ptr != (struct sec_list *)0; sec_ptr = sec_ptr->next)
	{
	  if (strcmp (name, sec_ptr->section) == 0)
	    break;
	}

      if (!sec_ptr)
	return;
    }

  switch ((int)sec_hdr->sh_type)
    {
    default:
      sec_type = stringify (sec_hdr->sh_type, 0, 'd', 0, "type=", "");
      sec_func = print_section_raw;
      break;

    case SHT_NULL:	sec_type = "none";	sec_func = print_section_raw;		break;
    case SHT_PROGBITS:	sec_type = "progbits";	sec_func = print_section_progbits;	break;
    case SHT_SYMTAB:	sec_type = "symtab";	sec_func = print_section_symtab;	break;
    case SHT_STRTAB:	sec_type = "strtab";	sec_func = print_section_string;	break;
    case SHT_RELA:	sec_type = "rela";	sec_func = print_section_rela;		break;
    case SHT_HASH:	sec_type = "hash";	sec_func = print_section_hash;		break;
    case SHT_DYNAMIC:	sec_type = "dynamic";	sec_func = print_section_dynamic;	break;
    case SHT_NOTE:	sec_type = "note";	sec_func = print_section_raw;		break;
    case SHT_NOBITS:	sec_type = "nobits";	sec_func = print_section_raw;		break;
    case SHT_REL:	sec_type = "rel";	sec_func = print_section_rel;		break;
    case SHT_SHLIB:	sec_type = "shlib";	sec_func = print_section_raw;		break;
    case SHT_DYNSYM:	sec_type = "dynsym";	sec_func = print_section_symtab;	break;
    }

  printf ("%sSect %s %-*s %-*s off=%s %s size=%s addr=%s align=%s flag=%s [%s%s%s] esize=%s info=%s link=%s\n",
	  (!summary || sec_index == 0) ? "\n" : "",
	  stringify (sec_index, sh_index_width, 'd', 0, "", ""),
	  sh_name_width, name,
	  sh_type_width, sec_type,
	  stringify (sec_hdr->sh_offset, sh_start_width, 'x', 0, "", ""),
	  stringify (sec_hdr->sh_offset + ((sec_hdr->sh_type == SHT_NOBITS) ? 0 : sec_hdr->sh_size),
		     sh_end_width, 'x', 0, "", ""),
	  stringify (sec_hdr->sh_size, sh_size_width, 'd', 0, "", ""),
	  stringify (sec_hdr->sh_addr, sh_addr_width, 'x', 0, "", ""),
	  stringify (sec_hdr->sh_addralign, sh_align_width, 'd', 0, "", ""),
	  stringify (sec_hdr->sh_flags, sh_flags_width, 'x', 0, "", ""),
	  (sec_hdr->sh_flags & SHF_EXECINSTR)	? "x" : "-",
	  (sec_hdr->sh_flags & SHF_ALLOC)	? "a" : "-",
	  (sec_hdr->sh_flags & SHF_WRITE)	? "w" : "-",
	  stringify (sec_hdr->sh_entsize, sh_esize_width, 'd', 0, "", ""),
	  stringify (sec_hdr->sh_info, sh_info_width, 'd', 0, "", ""),
	  stringify (sec_hdr->sh_link, sh_link_width, 'd', 0, "", ""));

  if (sec_hdr->sh_size > 0 && sec_hdr->sh_type != SHT_NOBITS && !summary)
    {
      putchar ('\n');
      (*sec_func) (sec_hdr);
    }
}

\f
/* Print program headers.  */

static void
print_proghdr (void)
{
  Elf32_Phdr *prog_hdr;
  const char *type;
  Elf32_Word ph_index;
  int width_index	= stringify_len (ehdr.e_phnum, 'd', 1);
  int width_vaddr	= 0;
  int width_vend	= 0;
  int width_type	= 0;
  int width_align	= 0;
  int width_flags	= 0;
  int width_offset	= 0;
  int width_offend	= 0;
  int width_filesz	= 0;
  int width_memsz	= 0;
  int len;
  char flags[4];
  char *load_start;
  char *load_end;
  const char *load_num;

  putchar ('\n');

  for (ph_index = 0; ph_index < ehdr.e_phnum; ph_index++)
    {
      prog_hdr = PROGHDR_PTR (ph_index);

      switch ((int)prog_hdr->p_type)
	{
	default:
	  type = stringify (prog_hdr->p_type, 0, 'd', 0, "type=", "");
	  break;

	case PT_NULL:	 type = "null,";	break;
	case PT_LOAD:	 type = "load,";	break;
	case PT_DYNAMIC: type = "dynamic,";	break;
	case PT_INTERP:	 type = "interp,";	break;
	case PT_NOTE:	 type = "note,";	break;
	case PT_SHLIB:	 type = "shlib,";	break;
	case PT_PHDR:	 type = "proghdr,";	break;
	}

      len = strlen (type);
      if (len > width_type)
	width_type = len;

      len = stringify_len (prog_hdr->p_vaddr, 'x', 1);
      if (len > width_vaddr)
	width_vaddr = len;

      len = stringify_len (prog_hdr->p_vaddr + prog_hdr->p_memsz, 'x', 1);
      if (len > width_vend)
	width_vend = len;

      len = stringify_len (prog_hdr->p_align, 'd', 1);
      if (len > width_align)
	width_align = len;

      len = stringify_len (prog_hdr->p_flags, 'x', 1);
      if (len > width_flags)
	width_flags = len;

      len = stringify_len (prog_hdr->p_offset, 'x', 1);
      if (len > width_offset)
	width_offset = len;

      len = stringify_len (prog_hdr->p_offset + prog_hdr->p_filesz, 'x', 1);
      if (len > width_offend)
	width_offend = len;

      len = stringify_len (prog_hdr->p_filesz, 'd', 1);
      if (len > width_filesz)
	width_filesz = len;

      len = stringify_len (prog_hdr->p_memsz, 'd', 1);
      if (len > width_memsz)
	width_memsz = len;
    }

  for (ph_index = 0; ph_index < ehdr.e_phnum; ph_index++)
    {
      prog_hdr = PROGHDR_PTR (ph_index);

      switch ((int)prog_hdr->p_type)
	{
	default:
	  type = stringify (prog_hdr->p_type, 0, 'd', 0, "type=", "");
	  break;

	case PT_NULL:	 type = "null,";	break;
	case PT_LOAD:	 type = "load,";	break;
	case PT_DYNAMIC: type = "dynamic,";	break;
	case PT_INTERP:	 type = "interp,";	break;
	case PT_NOTE:	 type = "note,";	break;
	case PT_SHLIB:	 type = "shlib,";	break;
	case PT_PHDR:	 type = "proghdr,";	break;
	}

      flags[0] = (prog_hdr->p_flags & PF_R) ? 'r' : '-';
      flags[1] = (prog_hdr->p_flags & PF_W) ? 'w' : '-';
      flags[2] = (prog_hdr->p_flags & PF_X) ? 'x' : '-';
      flags[3] = '\0';

      printf ("Hdr %s: %-*s vaddr= %s %s, align= %s, flags= %s [%s], off= %s %s, filesz= %s, memsz= %s\n",
	      stringify (ph_index, width_index, 'd', 0, "", ""),
	      width_type, type,
	      stringify (prog_hdr->p_vaddr, width_vaddr, 'x', 0, "", ""),
	      stringify (prog_hdr->p_vaddr + prog_hdr->p_memsz, width_vend, 'x', 0, "", ""),
	      stringify (prog_hdr->p_align, width_align, 'd', 0, "", ""),
	      stringify (prog_hdr->p_flags, width_flags, 'x', 0, "", ""),
	      flags,
	      stringify (prog_hdr->p_offset, width_offset, 'x', 0, "", ""),
	      stringify (prog_hdr->p_offset + prog_hdr->p_filesz, width_offend, 'x', 0, "", ""),
	      stringify (prog_hdr->p_filesz, width_filesz, 'd', 0, "", ""),
	      stringify (prog_hdr->p_memsz,  width_memsz,  'd', 0, "", ""));

      if ((int)prog_hdr->p_type == PT_LOAD)
	{
	  load_num = stringify (ph_index, 0, 'd', 0, "", "");
	  len = strlen (load_num) + strlen (flags);

	  load_start = (char *) xmalloc (sizeof("Load cmd #, region start []") + len);
	  sprintf (load_start, "Load cmd #%s, region start [%s]", load_num, flags);
	  add_layout (&vm_layout, load_start, prog_hdr->p_vaddr, 0, layout_free_no);
	  add_layout (&file_layout, load_start, prog_hdr->p_offset, 0, layout_free_yes);

	  load_end = (char *) xmalloc (sizeof("Load cmd #, region end []") + len);
	  sprintf (load_end, "Load cmd #%s, region end [%s]", load_num, flags);
	  add_layout (&vm_layout, load_end, prog_hdr->p_vaddr + prog_hdr->p_memsz, 0, layout_free_no);
	  add_layout (&file_layout, load_end, prog_hdr->p_offset + prog_hdr->p_filesz, 0, layout_free_yes);
	}
    }
}

\f
/* Scan for linker generated symbols, and put them in the VM layout */

static void
find_linker_symbols (void)
{
  int		 i;
  Elf32_Shdr	*sec_hdr;
  const char	*name;
  Elf32_Sym	*ptr;
  Elf32_Sym	*endptr;
  layout_t	*layout_ptr;

  for (i = 0; i < ehdr.e_shnum; i++)
    {
      sec_hdr = SEC_PTR (i);
      if ((int)sec_hdr->sh_type != SHT_SYMTAB && (int)sec_hdr->sh_type != SHT_DYNSYM)
	continue;

      ptr    = (Elf32_Sym *)(file_bytes + sec_hdr->sh_offset);
      endptr = (Elf32_Sym *)((char *)ptr + sec_hdr->sh_size);

      for ( ; ptr < endptr; ptr++)
	{
	  name = elf_string (sec_hdr->sh_link, ptr->st_name);
	  if (name[0] == '\0')
	    continue;

	  switch ((name[0] << 16) | (name[1] << 8) | name[2])
	    {
	    default:
	      continue;

	    case (('_' << 16) | ('G' << 8) | 'L'):
	      if (strcmp (name, "_GLOBAL_OFFSET_POINTER") != 0)
		continue;
	      break;

	    case (('_' << 16) | ('e' << 8) | 'n'):
	      if (strcmp (name, "_end") != 0)
		continue;
	      break;

	    case (('_' << 16) | ('e' << 8) | 'd'):
	      if (strcmp (name, "_edata") != 0)
		continue;
	      break;

	    case (('_' << 16) | ('e' << 8) | 't'):
	      if (strcmp (name, "_etext") != 0)
		continue;
	      break;

	    case (('e' << 16) | ('n' << 8) | 'd'):
	      if (strcmp (name, "end") != 0)
		continue;
	      break;

	    case (('e' << 16) | ('d' << 8) | 'a'):
	      if (strcmp (name, "edata") != 0)
		continue;
	      break;

	    case (('e' << 16) | ('t' << 8) | 'e'):
	      if (strcmp (name, "etext") != 0)
		continue;
	      break;
	    }

	  for (layout_ptr = vm_layout; layout_ptr != (layout_t *)0; layout_ptr = layout_ptr->next)
	    {
	      if (layout_ptr->name == name)
		break;

	      if (layout_ptr->name[0] == name[0] && strcmp (layout_ptr->name, name) == 0)
		break;
	    }

	  if (layout_ptr == (layout_t *)0)
	    add_layout (&vm_layout, name, ptr->st_value, 0, layout_free_no);
	}
    }
}

\f
int
main(int argc, char *argv[])
{
  int argnum;
  Elf32_Word i;
  int letter;
  int ch;

  while ((letter = getopt (argc, argv, "deln:pst:vx")) != EOF)
    {
      switch (letter)
	{
	default:
	  errors++;
	  break;

	case 'd':		/* invoke objdump to disassemble text sections */
	  objdump = 1;
	  break;

	case 'e':		/* put linker generated symbols in the VM layout */
	  linker_symbols = 1;
	  break;

	case 'l':		/* skip printing layout information */
	  no_layout = 1;
	  break;

	case 'n':		/* print a specific section */
	  {
	    struct sec_list *p = (struct sec_list *) xmalloc (sizeof (struct sec_list));
	    p->next = sections;
	    p->section = optarg;
	    sections = p;
	    break;
	  }

	case 'p':		/* skip printing the progbits section body */
	  no_progbits = 1;
	  break;

	case 's':		/* print just the summary information */
	  summary = 1;
	  break;

	case 't':		/* specify output format */
	  format = ch = tolower (*optarg);
	  if (optarg[1] != '\0' || (ch != 'd' && ch != 'x' && ch != 'o' && ch != 'u' & ch != 'i'))
	    {
	      fprintf (stderr, "-t %s is not 'i', 'd', 'o', 'u', or 'x'\n", optarg);
	      errors++;
	    }
	  break;

	case 'v':		/* print version string */
	  print_vers = 1;
	  break;

	case 'x':		/* string table pointer is invalid */
	  ignore_string = 1;
	  break;
	}
    }

  if (errors || optind >= argc)
    {
      fprintf (stderr, "Calling sequence:\n\n");
      fprintf (stderr, "\telfdump [-n name]... [-delpsx] [-t format] files...\n");
      fprintf (stderr, "\nwhere:\n\n");
      fprintf (stderr, "-d\tInvoke objdump -d for executable sections.\n");
      fprintf (stderr, "-e\tPut the linker generated symbols in the vm layout.\n");
      fprintf (stderr, "-l\tDo not print file/VM layout information.\n");
      fprintf (stderr, "-n name\tOnly print section 'name'\n");
      fprintf (stderr, "-p\tDo not print the body of a progbits section.\n");
      fprintf (stderr, "-s\tOnly print section header summary information.\n");
      fprintf (stderr, "-t fmt\tSpecify output format for numbers (i, d, o, u, or x).\n");
      fprintf (stderr, "-v\tPrint version number.\n");
      fprintf (stderr, "-x\tAssume the string table is corrupt.\n");
      return 1;
    }

  if (print_vers)
    printf ("%s\n\n", elfdump_what_string);

  for (argnum = optind; (file = argv[argnum]) != (char *)0; argnum++)
    {
      if (argnum > optind)
	printf ("\f====================\n\n");

      /* Map in the file.  */
      if (stat (file, &file_stat) < 0
	  || (file_fd = open (file, O_RDONLY)) < 0
	  || (file_bytes = (uchar_t *) mmap ((caddr_t)0,
					     (size_t)file_stat.st_size,
					     PROT_READ,
					     MAP_PRIVATE|MAP_FILE|MAP_VARIABLE,
					     file_fd,
					     (off_t)0)) == (uchar_t *)-1)
	{
	  perror (file);
	  errors++;
	  continue;
	}

      ehdr = *(Elf32_Ehdr *)file_bytes;
      if (file_stat.st_size < sizeof (ehdr) || memcmp (ehdr.e_ident, "\177ELF", 4) != 0)
	{
	  fprintf (stderr, "%s is not an ELF file.\n", file);
	  errors++;
	  continue;
	}

      if (ehdr.e_ehsize != sizeof (ehdr))
	{
	  fprintf (stderr, "%s elf header is %s bytes, expected %s\n", file,
		   stringify (ehdr.e_ehsize, 0, 'd', 0, "", ""),
		   stringify (sizeof (ehdr), 0, 'd', 0, "", ""));

	  errors++;
	  continue;
	}

      add_layout (&file_layout, "Global header", 0, sizeof (ehdr), layout_free_no);

      if (ehdr.e_shnum)
	add_layout (&file_layout, "Section headers", ehdr.e_shoff, ehdr.e_shnum * ehdr.e_shentsize, layout_free_no);

      if (ehdr.e_phnum)
	add_layout (&file_layout, "Program headers", ehdr.e_phoff, ehdr.e_phnum * ehdr.e_phentsize, layout_free_no);

      printf ("File: %s", file);
      switch (ehdr.e_type)
	{
	default:	printf (", type %d", ehdr.e_type);		break;
	case ET_NONE:	printf (", no file type");			break;
	case ET_REL:	printf (", relocatable");			break;
	case ET_EXEC:	printf (", executable");			break;
	case ET_DYN:	printf (", shared object");			break;
	case ET_CORE:	printf (", core file");				break;
	case ET_PROF:	printf (", profile output");			break;
	}

      switch (ehdr.e_machine)
	{
	default:	printf (", machine %d", ehdr.e_machine);	break;
	case EM_NONE:	printf (", no machine");			break;
	case EM_M32:	printf (", we 32100");				break;
	case EM_SPARC:	printf (", sparc");				break;
	case EM_386:	printf (", i386");				break;
	case EM_68K:	printf (", m68k");				break;
	case EM_88K:	printf (", m88k");				break;
	case EM_860:	printf (", i860");				break;
	case EM_MIPS:	printf (", mips");				break;
	}

      switch (ehdr.e_ident[EI_CLASS])
	{
	default:	 printf (", class %d", ehdr.e_ident[EI_CLASS]);	break;
	case ELFCLASS32: printf (", 32-bit");				break;
	case ELFCLASS64: printf (", 64-bit");				break;
	}

      switch (ehdr.e_ident[EI_DATA])
	{
	default:	  printf (", data %d", ehdr.e_ident[EI_DATA]);	break;
	case ELFDATA2LSB: printf (", little endian");			break;
	case ELFDATA2MSB: printf (", big endian");			break;
	}

      printf ("\nversion = %s, elf header size = %s, entry = %s\n",
	      stringify (ehdr.e_version, 0, 'd', 0, "", ""),
	      stringify (ehdr.e_ehsize, 0, 'd', 0, "", ""),
	      stringify (ehdr.e_entry, 8, 'x', 0, "", ""));

      printf ("flags = %s, section header string index = %s\n",
	      stringify (ehdr.e_flags, 8, 'x', 0, "", ""),
	      stringify (ehdr.e_shstrndx, 0, 'd', 0, "", ""));

      printf ("# sections = %s, section element size = %s, section offset = %s\n",
	      stringify (ehdr.e_shnum, 2, 'd', 0, "", ""),
	      stringify (ehdr.e_shentsize, 2, 'd', 0, "", ""),
	      stringify (ehdr.e_shoff, 6, 'd', 0, "", ""));

      printf ("# headers  = %s, header  element size = %s, header  offset = %s\n\n",
	      stringify (ehdr.e_phnum, 2, 'd', 0, "", ""),
	      stringify (ehdr.e_phentsize, 2, 'd', 0, "", ""),
	      stringify (ehdr.e_phoff, 6, 'd', 0, "", ""));

      /* Print each of the sections now.  */
      for (i = 0; i < ehdr.e_shnum; i++)
	size_section (i);

      for (i = 0; i < ehdr.e_shnum; i++)
	print_section (i);

      /* Print each of the program headers now.  */
      if (ehdr.e_phnum != 0)
	print_proghdr ();

      /* Print the layout information if desired */
      if (!no_layout)
	{
	  print_layout (file_layout, "ELF file");

	  if (ehdr.e_phnum)
	    {
	      if (linker_symbols)
		find_linker_symbols ();

	      print_layout (vm_layout, "Virtual Memory");
	    }
	}

      if (file_bytes != (uchar_t *)-1)
	munmap ((caddr_t)file_bytes, (size_t)file_stat.st_size);

      if (file_fd >= 0)
	close (file_fd);

      if (file_layout)
	free_layout (&file_layout);

      if (vm_layout)
	free_layout (&vm_layout);
    }

  return (errors) ? 1 : 0;
}


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Elfdump
  1994-11-11  8:39 Elfdump Michael Meissner
@ 1994-11-11 13:42 ` Richard Stallman
  1994-11-11 14:58   ` Elfdump Arthur Kreitman
  1994-11-14  6:50   ` Elfdump Michael Meissner
  0 siblings, 2 replies; 5+ messages in thread
From: Richard Stallman @ 1994-11-11 13:42 UTC (permalink / raw)
  To: meissner; +Cc: gas2, bfd

     * fee, provided that the above copyright notice appears in all copies and
     * that both the copyright notice and this permission notice appear in
     * supporting documentation.

Requirements like this about supporting documentation are somewhat
obnoxious -- as well as legally unenforceable in the US from what I've
heard -- because they impose a requirement on separate and independent
works.

So I've decided we should avoid such programs except when they are
very necessary.

Is there any possibility you could remove that clause from the terms?




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Elfdump
  1994-11-11 13:42 ` Elfdump Richard Stallman
@ 1994-11-11 14:58   ` Arthur Kreitman
  1994-11-12  8:41     ` Elfdump Richard Stallman
  1994-11-14  6:50   ` Elfdump Michael Meissner
  1 sibling, 1 reply; 5+ messages in thread
From: Arthur Kreitman @ 1994-11-11 14:58 UTC (permalink / raw)
  To: rms; +Cc: meissner, gas2, bfd

>   Date: Fri, 11 Nov 94 16:41:59 -0500
>   From: rms@gnu.ai.mit.edu (Richard Stallman)
>
>	* fee, provided that the above copyright notice appears in all copies and
>	* that both the copyright notice and this permission notice appear in
>	* supporting documentation.
>
>   Requirements like this about supporting documentation are somewhat
>   obnoxious -- as well as legally unenforceable in the US from what I've
>   heard -- because they impose a requirement on separate and independent
>   works.

  Is a binary that comes from your source code a separate work?  


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Elfdump
  1994-11-11 14:58   ` Elfdump Arthur Kreitman
@ 1994-11-12  8:41     ` Richard Stallman
  0 siblings, 0 replies; 5+ messages in thread
From: Richard Stallman @ 1994-11-12  8:41 UTC (permalink / raw)
  To: congrunt!artk; +Cc: meissner, gas2, bfd

    >   Date: Fri, 11 Nov 94 16:41:59 -0500
    >   From: rms@gnu.ai.mit.edu (Richard Stallman)
    >
    >	* fee, provided that the above copyright notice appears in all copies and
    >	* that both the copyright notice and this permission notice appear in
    >	* supporting documentation.
    >
    >   Requirements like this about supporting documentation are somewhat
    >   obnoxious -- as well as legally unenforceable in the US from what I've
    >   heard -- because they impose a requirement on separate and independent
    >   works.

      Is a binary that comes from your source code a separate work?  

No, but I don't follow the point.  The issue I'm talking about has to
do with documentation, not binaries.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Elfdump
  1994-11-11 13:42 ` Elfdump Richard Stallman
  1994-11-11 14:58   ` Elfdump Arthur Kreitman
@ 1994-11-14  6:50   ` Michael Meissner
  1 sibling, 0 replies; 5+ messages in thread
From: Michael Meissner @ 1994-11-14  6:50 UTC (permalink / raw)
  To: rms; +Cc: gas2, bfd

|      * fee, provided that the above copyright notice appears in all copies and
|      * that both the copyright notice and this permission notice appear in
|      * supporting documentation.
| 
| Requirements like this about supporting documentation are somewhat
| obnoxious -- as well as legally unenforceable in the US from what I've
| heard -- because they impose a requirement on separate and independent
| works.
| 
| So I've decided we should avoid such programs except when they are
| very necessary.
| 
| Is there any possibility you could remove that clause from the terms?

No.  I was just offering elfdump in case it was useful.


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~1994-11-14  6:50 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1994-11-11  8:39 Elfdump Michael Meissner
1994-11-11 13:42 ` Elfdump Richard Stallman
1994-11-11 14:58   ` Elfdump Arthur Kreitman
1994-11-12  8:41     ` Elfdump Richard Stallman
1994-11-14  6:50   ` Elfdump Michael Meissner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).