aboutsummaryrefslogblamecommitdiff
path: root/decomp/symtab.c
blob: 778e9e7a9b322611a546fb188e472706f8d7ec7a (plain) (tree)















































































































































































































































































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