/* symtab.c * This file is part of decomp - a disassembler. This file contains * routines for parsing the symbol table. * * Copyright (C) 2001 Jonathan duSaint * * Started around 10 December 2001. */ #include #include #include #include "decomp.h" #define LOCAL_LABEL_SIZE 32 /* create_mem_ref * Internal subroutine. Creates a symbol of type TYPE at virtual address * VADDR and inserts it into SYMTAB, returning the new symbol. */ struct symtab * create_mem_ref (hash_t symtab, unsigned long vaddr, enum symbol_type type) { static unsigned long label_value = 0; struct symtab *sym; sym = xmalloc (sizeof (struct symtab)); sym->name = xmalloc (LOCAL_LABEL_SIZE); sprintf (sym->name, "L%ld", label_value++); sym->address = vaddr; sym->size = 0; sym->idx = 0; sym->type = type; hash_add (symtab, vaddr, sym); return sym; } /* create_jump_ref * This is called when there is a jmp or a call instruction with * an unnamed destination. A local label is created and * inserted into SYMTAB with the key VADDR. */ struct symtab * create_jump_ref (hash_t symtab, unsigned long vaddr) { return create_mem_ref (symtab, vaddr, LOCAL_UNKNOWN); } /* create_data_ref * This is used to create a label for unnamed data that is referenced * somewhere in code. */ struct symtab * create_data_ref (hash_t symtab, unsigned long vaddr) { return create_mem_ref (symtab, vaddr, LOCAL_DATA); } #define N_SECTION_LIMITS 64 struct section_limits { unsigned long low; unsigned long high; }; struct section_limits **limits = NULL; /* check_is_data * If VADDR is a virtual address value that is within a data section, * then return 1, otherwise return 0. */ int check_is_data (unsigned long vaddr) { int k = -1; if (debug) printf ("checking limit %#lx... ", vaddr); while (limits[++k] != NULL) if (vaddr >= limits[k]->low && vaddr <= limits[k]->high) { if (debug) printf ("ok\n"); return 1; } if (debug) printf ("no\n"); return 0; } /* put_data_limits * Store the start and end virtual address of a section so that value * references in code can be determined to be in a data section or not. */ void put_data_limits (unsigned long low, unsigned long high) { int k = -1; if (limits == NULL) limits = xmalloc (sizeof (struct section_limits *) * N_SECTION_LIMITS); /* find an empty slot */ while (limits[++k] != NULL); limits[k] = xmalloc (sizeof (struct section_limits)); limits[k]->low = low; limits[k]->high = high; if (debug) printf ("adding section limits: %#lx - %#lx\n", low, high); } /* has_precedence * Determine if SYM has precedence over OLD_SYM. */ int has_precedence (struct symtab *sym, struct symtab *old_sym) { return ((sym->type < LOCAL_UNKNOWN && old_sym->type >= LOCAL_UNKNOWN) || (sym->size > 0 && old_sym->size == 0)); } /* read_strtab * Read the string table from the object file, if it exists. */ char * read_strtab (struct file_info *fi) { switch (fi->file_type) { case FT_ELF: return elf_read_strtab (fi); /* case FT_AOUT: */ /* return aout_read_strtab (fi); */ case FT_PE: return pe_read_strtab (fi); case FT_NE: return ne_read_strtab (fi); case FT_MZ: return mz_read_strtab (fi); /* case FT_COM: */ /* return com_read_strtab (fi); */ default: ; } return NULL; } /* get_next_symbol * Get the next symbol from the object file and return it as a struct symtab. */ struct symtab * get_next_symbol (struct file_info *fi, char *strtab) { switch (fi->file_type) { case FT_ELF: return elf_get_next_symbol (fi, strtab); /* case FT_AOUT: */ /* return aout_get_next_symbol (fi, strtab); */ case FT_PE: return pe_get_next_symbol (fi, strtab); case FT_NE: return ne_get_next_symbol (fi, strtab); case FT_MZ: return mz_get_next_symbol (fi, strtab); /* case FT_COM: */ /* return com_get_next_symbol (fi, strtab); */ default: ; } return NULL; } /* read_symtab * Read the symbol table in the object file if it exists. */ hash_t read_symtab (struct file_info *fi) { unsigned long k; char *strtab; hash_t symtab; struct symtab *sym, *old_sym; symtab = hash_new (0); /* if this program has been stripped, return an empty hash table */ if (fi->symtab == NULL) return symtab; if (fi->symtab->section_name == 0) return symtab; /* read in the strtab */ strtab = read_strtab (fi); /* read in the symtab */ fseek (fi->fp, fi->symtab->section_offset, SEEK_SET); for (k = 0; k < fi->symtab->section_size / fi->symtab->entsize; k++) { sym = get_next_symbol (fi, strtab); if (sym == NULL) continue; /* check if there is already a symbol at this location */ /* check the logic here */ old_sym = hash_get (symtab, sym->address); if (old_sym != NULL) { if (has_precedence (sym, old_sym)) { hash_remove (symtab, old_sym->address); hash_add (symtab, sym->address, sym); } } else hash_add (symtab, sym->address, sym); } xfree (strtab); /*if (debug) hash_print (symtab);*/ return (symtab); } /* clean_symtab * Clean up and deallocate the symbol table. */ void clean_symtab (hash_t symtab) { unsigned long key; struct symtab *sym; hash_walk (NULL); while ((key = hash_walk (symtab)) != HASH_END) { sym = hash_remove (symtab, key); xfree (sym->name); xfree (sym); } hash_delete (symtab); }