aboutsummaryrefslogblamecommitdiff
path: root/decomp/decomp.c
blob: 07935267cfe868d0c09f82cb9589f6830eb126a5 (plain) (tree)























































































































































































































































































































                                                                               
/* decomp.c
 * Decompile an executable file.
 *
 * Copyright (C) 2001 Jonathan duSaint <dusaint@earthlink.net>
 *
 * Started around 15 November 2001.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <stdint.h>

/*#include <signal.h>*/

#include "decomp.h"


char pname[] = PACKAGE_NAME;
char version[] = PACKAGE_VERSION;
char author[] = AUTHOR;
char author_email[] = AUTHOR_EMAIL;


int debug = 0;
int print_address = 0;
int print_opcode = 0;
int print_prefix = 0;
enum syntax syntax = INTEL;
int host_endian = ELITTLE, target_endian = ELITTLE; /* handy default */

void
write_file_header (FILE *fp, const char *source_file, const char *file_name)
{
  fprintf (fp,
	   "\t; %s\n"
	   "\t; Decompiled by Decomp from %s.\n"
	   "\t; In case of an incorrect decompilation, let the author know.\n"
	   "\n",
	   file_name, source_file);
}


/* get_output_name
 * Called when an output file has not been specified with -o.  Appends
 * DEFAULT_OUTPUT_EXTENSION on to the file name of the object file.  The
 * returned memory must be freed.
 */
char *
get_output_name (const char *file_name)
{
  char *output_name;
  char output_extension[] = DEFAULT_OUTPUT_EXTENSION;

  output_name = xmalloc (strlen (file_name) + strlen (output_extension) + 1);

  sprintf (output_name, "%s%s", file_name, output_extension);

  return (output_name);
}


/* byte_reverse
 * Convert from big to little endian and vice-versa.
 */
void
byte_reverse (void *ptr, size_t size, size_t nmemb)
{
  uint8_t *buf = (uint8_t *)ptr;

  if (size == 1 || nmemb == 0) return;

  do
    {
      register size_t low = 0, high = size - 1;

      while (high > low)
	{
	  register uint8_t tmp;

	  tmp = buf[low];
	  buf[low] = buf[high];
	  buf[high] = tmp;

	  high--; low++;
	}

      buf += size;
    }
  while (--nmemb);
}


/* d_read
 * Similar in functionality to fread, except ensures that > 0 bytes are
 * read and accounts for byte order.
 */
void
d_read (void *ptr, size_t size, size_t nmemb, FILE *stream, int as_is)
{
  if (fread (ptr, size, nmemb, stream) <= 0) error_out (ERR_FILE_READ);

  if (as_is) return;

  /* if (host_endian .xor. target_endian) */
  if ((host_endian || target_endian) - (host_endian && target_endian))
    byte_reverse (ptr, size, nmemb);
}


int
get_host_endian (void)
{
  union {
    char c[4];
    uint32_t i;
  } x;

  memset (&x, 0, 4); /* just to be sure */

  x.i = 1;

  if (x.c[0] != 0) return ELITTLE;
  else return EBIG;
}


void
print_version (void)
{
  printf ("%s %s\n"
	  "Copyright (C) 2001 %s %s\n"
	  "%s comes with NO WARRANTY, to the extent\n"
	  "permitted by law.  You may redistribute copies\n"
	  "of %s under the terms of the GNU General\n"
	  "Public License.  For more information about\n"
	  "these matters, see the file named COPYING.\n",
	  pname, version, author, author_email, pname, pname);

  exit (EXIT_SUCCESS);
}

void
print_help (void)
{
  printf ("Usage: decomp [OPTION]... <FILE>\n"
	  "Decompile FILE into a complete assembly language file\n"
	  "which can in turn be re-assembled.\n"
	  "\n"
	  "  -o, --output <file>      Place output in file.  The default is\n"
	  "                           to append .S to the input file name.\n"
	  "\n"
	  "  -h, --help               Print this message.\n"
	  "  -v, --version            Print version information.\n"
	  "\n"
	  "  -a, --address            Print the address of each instruction.\n"
	  "  -c, --opcode             Print the opcode of each instruction.\n"
	  "  -p, --prefix             Print any instruction prefixes.\n"
	  "\n"
	  "  -d, --debug              Print debugging information.\n"
	  "  -t, --trace              Turn on memory tracing.\n"
	  "\n"
	  "  -q, --queue-size <size>  Adjust the size of the output\n"
	  "                           instruction queue.  Only use this if\n"
	  "                           you know what you are doing.\n"
	  "IA-32 specific options:\n"
	  "  -s, --style <nasm|gas>   Select assembly language style.\n"
	  "                           Not yet implemented.\n"
	  "\n"
	  "Report bugs to %s %s\n",
	  author, author_email);
}


int
main (int argc, char *argv[])
{
  int k;
  char opt, *output_file_name = NULL;
  struct file_info fi;
  hash_t symtab;
  FILE *ofp;
  struct option options[] = {
    { "address",    no_argument,       NULL, 'a' },
    { "debug",      no_argument,       NULL, 'd' },
    { "help",       no_argument,       NULL, 'h' },
    { "opcode",     no_argument,       NULL, 'c' },
    { "output",     required_argument, NULL, 'o' },
    { "prefix",     no_argument,       NULL, 'p' },
    { "queue-size", required_argument, NULL, 'q' },
    { "style",      required_argument, NULL, 's' },
    { "trace",      no_argument,       NULL, 't' },
    { "version",    no_argument,       NULL, 'v' },
    { 0, 0, 0, 0 }
  };


  /* parse through argv, looking for options */
  while (1)
    {
      opt = getopt_long (argc, argv, "acdho:pq:s:tv", options, NULL);

      if (opt == EOF) break;

      switch (opt)
	{
	case 'a':
	  print_address = 1;
	  break;
	case 'c':
	  print_opcode = 1;
	  break;
	case 'd':
	  debug = 1;
	  break;
	case 'h':
	  print_help ();
	  exit (EXIT_SUCCESS);
	case 'o':
	  output_file_name = optarg;
	  break;
	case 'p':
	  print_prefix = 1;
	  break;
	case 'q':
	  max_queue_size = strtol (optarg, NULL, 0);
	  if (max_queue_size < 1)
	    {
	      fprintf (stderr, "DECOMP: invalid queue size - using default\n");
	      max_queue_size = MAX_OUTPUT_QUEUE_SIZE;
	    }
	  break;
	case 's':
	  if (!strcmp (optarg, "att"))
	    syntax = ATT;
	  else if (!strcmp (optarg, "intel"))
	    syntax = INTEL;
	  else
	    {
	      fprintf (stderr, "DECOMP: unknown disassembly syntax `%s'\n",
		       optarg);
	      exit (1);
	    }
	  break;
	case 't':
	  memory_trace = 1;
	  break;
	case 'v':
	  print_version ();
	  exit (EXIT_SUCCESS);
	case '?':
	  fprintf (stderr, "DECOMP: unknown option character `%c'\n", optopt);
	  exit (EXIT_FAILURE);
	default:
	  fprintf (stderr,
		   "DECOMP: ?? getopt returned character code 0x%x (%c) ??\n",
		   opt, opt);
	  exit (EXIT_FAILURE);
	}
    }

  /* all that's left in argv should be a file name to disassemble */
  if (argv[optind] == NULL) error_out (ERR_NEED_FILE_NAME);


  /* get host endian-ness and set target as the same for now */
  target_endian = host_endian = get_host_endian ();


  /* open_files (); */
  /* get_file_info (); */
  if (get_file_info (&fi, argv[optind])) error_out (ERR_FILE_INFO);

  /* write_asm_header (); */
  if (output_file_name == NULL)
    output_file_name = get_output_name (argv[optind]);

  ofp = fopen (output_file_name, "wt");
  if (ofp == NULL) error_out (ERR_FILE_OPEN);

  if (debug)
    setvbuf (ofp, NULL, _IONBF, 0);

  write_file_header (ofp, argv[optind], output_file_name);

  /* read the symtab */
  symtab = read_symtab (&fi);

  /* decode and write out the code sections */
  k = -1;
  while (fi.code_sections[++k])
    decode_code_section (&fi, fi.code_sections[k], symtab, ofp);

  /* decode and write out the data sections */
  k = -1;
  while (fi.data_sections[++k])
    decode_data_section (&fi, fi.data_sections[k], symtab, ofp);


  /* write_asm_footer (); */


  /* clean up */
  clean_symtab (symtab);
  fclose (ofp);
  if (clean_up_file (&fi)) error_out (ERR_CLEAN_UP);
  free_all ();

  return (0);
}