aboutsummaryrefslogblamecommitdiff
path: root/decomp/code.c
blob: b75706cbaf56dd916777cf2ec66633f97b994f05 (plain) (tree)






















































































































































































































































                                                                         
/* code.c
 * This file is part of Decomp - a decompiler.  This is the main code
 * decompilation routine.
 *
 * Copyright (C) 2001 Jonathan duSaint <dusaint@earthlink.net>
 *
 * Started around 1 December 2001.
 */

#include <stdio.h>
#include <string.h>

#include "decomp.h"


#define TAB_STOP 8
#define COMMENT_COLUMN 48


struct output_queue {
  struct output_queue *next;
  unsigned long vaddr;
  char *instruction;
  char *opcode;
};


struct output_queue *output_queue = NULL;
unsigned long items_in_queue = 0;
unsigned long max_queue_size = MAX_OUTPUT_QUEUE_SIZE;


/* count_length
 * Count the number of characters a line will take up.
 */
int
count_length (char *line)
{
  char *tmp;
  int tab_stop = TAB_STOP, count = 0, current = -1;

  while ((tmp = strchr (line, '\n')) != NULL) line = tmp + 1;

  while (line[++current])
    {
      if (line[current] == '\t')
	while (++count % tab_stop);
      else
	count++;
    }

  return count;
}


/* print_queue_head
 * Print the instruction at the head of the instruction queue with
 * its label, if any.
 */
void
print_queue_head (hash_t symtab, FILE *ofp)
{
  int len, k, comment_column = COMMENT_COLUMN, tab_stop = TAB_STOP;
  char *line;
  struct symtab *sym;
  struct output_queue *tmp;

  tmp = output_queue;
  output_queue = output_queue->next;
  items_in_queue--;

  line = tmp->instruction;

  sym = hash_get (symtab, tmp->vaddr);

  /* if there is a symbol at this virtual address */
  if (sym != NULL)
    {
      fprintf (ofp, "\n");

      /* if it's global, mark it as such */
      if (sym->type == GLOBAL_FUNCTION || sym->type == GLOBAL_UNKNOWN)
	fprintf (ofp, "\t.global\t%s\n", sym->name);

      /* if it's a function, say so */
      if (sym->type == GLOBAL_FUNCTION || sym->type == LOCAL_FUNCTION
	  || sym->type == UNKNOWN_FUNCTION)
	fprintf (ofp, "\t.type\t%s,@function\n", sym->name);

      if (sym->size != 0) fprintf (ofp, "\t.size\t%s, %ld\n", sym->name,
				   sym->size);

      fprintf (ofp, "%s:\n", sym->name);
    }


  fprintf (ofp, "%s", line);

  if (print_address || print_opcode)
    {
      len = count_length (line);

      if (len < comment_column - tab_stop)
	for (k = 0;
	     k < (((comment_column - tab_stop) - (len - len % tab_stop))
		  / tab_stop);
	     k++)
	  fputc ('\t', ofp);

      fprintf (ofp, "\t;");
    }

  if (print_address)
    fprintf (ofp, " %08lx", tmp->vaddr);

  if (print_opcode)
    {
      fputc (' ', ofp);

      for (k = 1; k < tmp->opcode[0] + 1; k++)
	fprintf (ofp, "%02hhx", tmp->opcode[k]);

      xfree (tmp->opcode);
    }

  fprintf (ofp, "\n");


  xfree (line);
  xfree (tmp);
}


/* output_line
 * Queue an output line, printing the line at the head of the queue
 * if the queue is full.
 */
void
output_line (hash_t symtab, FILE *ofp, char *line, unsigned long vaddr,
	     char *opcode)
{
  struct output_queue *new, *tmp;

  new = xmalloc (sizeof (struct output_queue));

  new->next = NULL;
  new->vaddr = vaddr;
  new->instruction = xmalloc (strlen (line) + 1);
  strcpy (new->instruction, line);
  new->opcode = opcode;

  /* if the queue is full, output one line */
  if (items_in_queue >= max_queue_size)
    print_queue_head (symtab, ofp);


  /* find the end of the output queue */
  if (output_queue == NULL)
    output_queue = new;
  else
    {
      tmp = output_queue;
      while (tmp->next != NULL) tmp = tmp->next;

      tmp->next = new;
    }

  items_in_queue++;
}

/* flush_output_queue
 * Print all lines remaining in the output queue.
 */
void
flush_output_queue (hash_t symtab, FILE *ofp)
{
  while (output_queue != NULL) print_queue_head (symtab, ofp);
}


/* decode_code_section
 * Disassemble a code section from the input file.
 */
void
decode_code_section (struct file_info *fi, struct section_info *s,
		     hash_t symtab, FILE *ofp)
{
  char *text, *line = NULL, *opcode = NULL;
  unsigned long old_pos = 0, pos = 0, flags = 0, vaddr;

  /* set flags */
  switch (fi->arch)
    {
    case MT_IA32:
      flags |= (fi->bit_size == BS_32) ? (F_IA32_A | F_IA32_D) : 0x0;
      flags |= (print_prefix) ? F_IA32_P : 0x0;
      break;
    case MT_ALPHA:
      /* nothing here yet */
    default:
      ;
    }

  fseek (fi->fp, s->section_offset, SEEK_SET);
  text = xmalloc (s->section_size);
  fread (text, 1, s->section_size, fi->fp);

  fprintf (ofp, "\n\t.section\t%s%s, @progbits\n\t.align\t%d\n\n",
	   s->section_name, s->flags, s->align);

  vaddr = s->load_address;

  while (pos < s->section_size)
    {
      switch (fi->arch)
	{
	case MT_IA32:
	  line = ia32_disassemble_instruction (text, &pos, flags, vaddr,
					       symtab);
	  break;
	case MT_ALPHA:
	  line = alpha_disassemble_instruction (text, &pos, flags, vaddr,
						symtab);
	  break;
	default:
	  ;
	}

      if (print_opcode)
	{
	  opcode = xmalloc (pos - old_pos + 1);
	  opcode[0] = pos - old_pos;
	  memcpy (opcode + 1, text + old_pos, opcode[0]);
	}

      output_line (symtab, ofp, line, vaddr, opcode);

      xfree (line);

      vaddr += pos - old_pos;
      old_pos = pos;
    }

  flush_output_queue (symtab, ofp);

  xfree (text);
}