/* 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);
}