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