/* symtab.c
* This file is part of decomp - a disassembler. This file contains
* routines for parsing the symbol table.
*
* Copyright (C) 2001 Jonathan duSaint <dusaint@earthlink.net>
*
* Started around 10 December 2001.
*/
#include <stdio.h>
#include <string.h>
#include <elf.h>
#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);
}