/* code.c * This file is part of Decomp - a decompiler. This is the main code * decompilation routine. * * Copyright (C) 2001 Jonathan duSaint * * Started around 1 December 2001. */ #include #include #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); }