/* objdump -- print information about an object file.
   Copyright (C) 1988, 1990 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <stdio.h>
#include <errno.h>
extern int errno;
#include "getopt.h"

#ifndef COFF_ENCAPSULATE
#include <a.out.h>
/* Tell a.out.gnu.h not to try to redefine some things.  */
#define __STRUCT_EXEC_OVERRIDE__
#define N_NLIST_DECLARED
#define N_RELOCATION_INFO_DECLARED
#define N_MAGIC(exec) ((exec).a_magic)
#include "a.out.gnu.h"
#else
#include "a.out.encap.h"
#endif

char *malloc ();

char *xmalloc ();
void dump_file ();
void dump_header ();
void dump_nstuff ();
void dump_reloc ();
void dump_reloc1 ();
void dump_sym ();
void error ();
void usage ();

/* Symbol table. */
struct nlist *symtbl;

/* Number of elements in `symtbl'. */
int nsyms;

/* String table. */
char *strtbl;

/* Number of elements in `strtbl'. */
int strsize;

/* Name this program was run with. */
char *program_name;

/* Name of object file currently being processed. */
char *filename;

void
read_symbols (execp, f)
     struct exec *execp;
     FILE *f;
{
  int i;
  struct nlist *sp;

  if (symtbl)
    return;
  nsyms = execp->a_syms / sizeof (struct nlist);
  if (nsyms == 0)
    return;

  symtbl = (struct nlist *) xmalloc (nsyms * sizeof (struct nlist));

  fseek (f, N_STROFF (*execp), 0);
  if (fread ((char *) &strsize, sizeof strsize, 1, f) != 1)
    error (1, errno, "%s: cannot read string table size", filename);
  strtbl = xmalloc (strsize);
  fseek (f, N_STROFF (*execp), 0);
  if (fread (strtbl, 1, strsize, f) != strsize)
    error (1, errno, "%s: cannot read string table", filename);

  fseek (f, N_SYMOFF (*execp), 0);
  if (fread ((char *) symtbl, sizeof (struct nlist), nsyms, f) != nsyms)
    error (1, errno, "%s: cannot read symbol table", filename);

  for (i = 0, sp = symtbl; i < nsyms; i++, sp++)
    {
      if (sp->n_un.n_strx == 0)
	sp->n_un.n_name = "";
      else if (sp->n_un.n_strx < 0 || sp->n_un.n_strx > strsize)
	sp->n_un.n_name = "<bad string table index>";
      else
	sp->n_un.n_name = strtbl + sp->n_un.n_strx;
    }
}

void
free_symbols ()
{
  if (symtbl)
    free (symtbl);
  symtbl = NULL;
  if (strtbl)
    free (strtbl);
  strtbl = NULL;
}

/* If nonzero, print header information. */
int print_header = 0;

/* If nonzero, print N_* information. */
int print_nstuff = 0;

/* If nonzero, print relocation information. */
int print_relocation = 0;

/* If nonzero, print symbol information. */
int print_symbols = 0;

/* Size of a page.  Required by N_DATADDR in a.out.gnu.h [VAX].  */
int page_size;

void
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int ind;
  int option_given = 0;
  static struct option long_options[] =
  {
    {"symbols", 0, &print_symbols, 1},
    {"relocation", 0, &print_relocation, 1},
    {"nstuff", 0, &print_nstuff, 1},
    {"header", 0, &print_header, 1},
    {NULL, 0, NULL, 0}
  };

  page_size = getpagesize ();

  program_name = argv[0];

  while ((c = getopt_long (argc, argv, "hnrt", long_options, &ind))
	 != EOF)
    {
      option_given = 1;
      switch (c)
	{
	case 0:
	  break;		/* we've been given a long option */
	case 't':
	  print_symbols = 1;
	  break;
	case 'r':
	  print_relocation = 1;
	  break;
	case 'n':
	  print_nstuff = 1;
	  break;
	case 'h':
	  print_header = 1;
	  break;
	default:
	  usage ();
	}
    }

  if (option_given == 0 || optind == argc)
    usage ();

  while (optind < argc)
    dump_file (argv[optind++]);

  exit (0);
}

void
dump_file (name)
     char *name;
{
  FILE *f;
  struct exec exec;

  printf ("%s:\n", name);
  f = fopen (name, "r");
  if (f == NULL)
    {
      error (0, errno, "%s", name);
      return;
    }
  filename = name;

#ifdef HEADER_SEEK
  HEADER_SEEK (f);
#endif
  if (fread ((char *) &exec, sizeof exec, 1, f) != 1)
    {
      error (0, errno, "%s: cannot read header", filename);
      return;
    }

  if (N_BADMAG (exec))
    {
      error (0, 0, "%s: Not an object file", filename);
      return;
    }

  if (print_header)
    dump_header (&exec);

  if (print_nstuff)
    dump_nstuff (&exec);

  if (print_symbols)
    dump_sym (&exec, f);

  if (print_relocation)
    dump_reloc (&exec, f);

  free_symbols ();
}

void
dump_header (execp)
     struct exec *execp;
{
  int x;

#if defined (__GNU_EXEC_MACROS__) && !defined (__STRUCT_EXEC_OVERRIDE__)
  printf ("magic: 0x%x (%o)", N_MAGIC (*execp), N_MAGIC (*execp));
  printf ("machine type: %d", N_MACHTYPE (*execp));
  printf ("flags: 0x%x", N_FLAGS (*execp));
#else /* non-gnu struct exec.  */
  printf ("magic: 0x%x (%o) ", execp->a_magic, execp->a_magic);
#endif /* non-gnu struct exec.  */
  printf ("text 0x%x ", execp->a_text);
  printf ("data 0x%x ", execp->a_data);
  printf ("bss 0x%x\n", execp->a_bss);
  printf ("nsyms %d", execp->a_syms / sizeof (struct nlist));
  x = execp->a_syms % sizeof (struct nlist);
  if (x)
    printf (" (+ %d bytes)", x);
  printf (" entry 0x%x ", execp->a_entry);
  printf ("trsize 0x%x ", execp->a_trsize);
  printf ("drsize 0x%x\n", execp->a_drsize);
}

void
dump_nstuff (execp)
     struct exec *execp;
{
  printf ("N_BADMAG %d\n", N_BADMAG (*execp));
  printf ("N_TXTOFF 0x%x\n", N_TXTOFF (*execp));
  printf ("N_SYMOFF 0x%x\n", N_SYMOFF (*execp));
  printf ("N_STROFF 0x%x\n", N_STROFF (*execp));
  printf ("N_TXTADDR 0x%x\n", N_TXTADDR (*execp));
  printf ("N_DATADDR 0x%x\n", N_DATADDR (*execp));
}

void
dump_sym (execp, f)
     struct exec *execp;
     FILE *f;
{
  int i;
  struct nlist *sp;

  read_symbols (execp, f);
  if (nsyms == 0)
    {
      printf ("no symbols\n");
      return;
    }

  printf ("%3s: %4s %5s %4s %8s\n",
	  "#", "type", "other", "desc", "val");
  for (i = 0, sp = symtbl; i < nsyms; i++, sp++)
    {
      printf ("%3d: %4x %5x %4x %8x %s\n",
	      i,
	      sp->n_type & 0xff,
	      sp->n_other & 0xff,
	      sp->n_desc & 0xffff,
	      sp->n_value,
	      sp->n_un.n_name);
    }
}

void
dump_reloc (execp, f)
     struct exec *execp;
     FILE *f;
{
  read_symbols (execp, f);
  if (execp->a_trsize)
    {
      printf ("text reloc\n");
      dump_reloc1 (execp, f, N_TRELOFF (*execp), execp->a_trsize);
    }
  if (execp->a_drsize)
    {
      printf ("data reloc\n");
      dump_reloc1 (execp, f, N_DRELOFF (*execp), execp->a_drsize);
    }
}

void
dump_reloc1 (execp, f, off, size)
     struct exec *execp;
     FILE *f;
     int off;
     int size;
{
#if !defined(sun) || !defined(sparc)
  int nreloc;
  struct relocation_info reloc;
  int i;

  nreloc = size / sizeof (struct relocation_info);

  printf ("%3s: %3s %8s %4s\n", "#", "len", "adr", "sym");
  fseek (f, off, 0);
  for (i = 0; i < nreloc; i++)
    {
      if (fread ((char *) &reloc, sizeof reloc, 1, f) != 1)
	{
	  error (0, errno, "%s: cannot read relocation information",
		 filename);
	  return;
	}
      printf ("%3d: %3d %8x ", i, 1 << reloc.r_length,
	      reloc.r_address);

      if (reloc.r_extern)
	{
	  printf ("%4d ", reloc.r_symbolnum);
	  if (reloc.r_symbolnum < nsyms)
	    printf ("%s ", symtbl[reloc.r_symbolnum].n_un.n_name);
	}
      else
	{
	  printf ("     ");
	  switch (reloc.r_symbolnum & ~N_EXT)
	    {
	    case N_TEXT:
	      printf (".text ");
	      break;
	    case N_DATA:
	      printf (".data ");
	      break;
	    case N_BSS:
	      printf (".bss ");
	      break;
	    case N_ABS:
	      printf (".abs ");
	      break;
	    default:
	      printf ("base %x ", reloc.r_symbolnum);
	      break;
	    }
	}
      if (reloc.r_pcrel)
	printf ("PCREL ");
#if 0
      if (reloc.r_pad)
	printf ("PAD %x ", reloc.r_pad);
#endif
      printf ("\n");
    }
#endif
}

/* Allocate N bytes of memory dynamically, with error checking.  */

char *
xmalloc (n)
     unsigned n;
{
  char *p;

  p = malloc (n);
  if (p == 0)
    error (1, 0, "virtual memory exhausted");
  return p;
}

#ifdef USG
int
getpagesize ()
{
  return 4096;
}
#endif

#if defined(sun) && defined(sparc)
int
getpagesize ()
{
  return 8192;
}
#endif

void
usage ()
{
  fprintf (stderr, "\
Usage: %s [-hnrt] [+header] [+nstuff] [+relocation] [+symbols] objfile...\n",
	   program_name);
  exit (1);
}
