/*
** symbol.c for libelfsh
** 
** Started on  Mon Feb 26 04:11:46 2001 mayhem
** Last update Sun Mar 24 09:22:24 2002 mayhem
*/
#include "libelfsh.h"






/* Return the symbol size */
int	elfsh_get_symbol_size(Elf32_Sym *s)
{
  return (s->st_size);
}



/* Return the symbol binding giving its index in the symbind array */
u_int	elfsh_get_symbol_bind(Elf32_Sym *s)
{
  return (ELF32_ST_BIND(s->st_info));
}



/* Return the symbol type name giving its index in the symtype array */
u_int	elfsh_get_symbol_type(Elf32_Sym *s)
{
  return (ELF32_ST_TYPE(s->st_info));
}




/* Return the symbo offset giving its Elf32_Sym entry */
void	*elfsh_get_symbol_offset(Elf32_Sym *s)
{
  return ((void *) s->st_value);
}




/* Retreive the file offset giving the virtual address */
int		elfsh_get_symbol_foffset(Elfsh32_Obj *file, Elf32_Sym *sym)
{
  Elfsh32_Sect	*sect;
  char		*name;


  /* If the symbol is a section, then look at the sht instead */
  if (elfsh_get_symbol_type(sym) == STT_SECTION)
    {
      name = elfsh_get_symbol_name(file, sym);
      sect = elfsh_get_section_by_name(file, name, NULL, NULL, NULL);
      return (sect ? elfsh_get_section_foffset(sect->shdr) : 0);
    }


  /* get our parent section and compute the file offset */
  if (!sym || !file || !sym->st_value)
    return (0);

  sect = elfsh_get_parent_section(file, sym->st_value, NULL);
  if (!sect)
    return (0);
  
  return (sect->shdr->sh_offset + (sym->st_value - sect->shdr->sh_addr));
}





/* Return the symbol name giving its index in the symbol string table */
char	*elfsh_get_symbol_name(Elfsh32_Obj *file, Elf32_Sym *s)
{
  Elf32_Shdr	*sct;
  char		*str;

  if (!s || !elfsh_get_symtab(file, NULL))
    return (NULL);

  /* Use the section string table if the symbol is a section */
  if (elfsh_get_symbol_type(s) == STT_SECTION)
    {
      sct = elfsh_get_shtentry_from_sym(file, s);
      return (sct ? elfsh_get_section_name(file, sct) : NULL);
    }

  /* Else use the symbol string table */
  str = file->strtab->data;
  return (str + s->st_name);
}






/* Just copy the symtab . The returned symbol table need to be freed */
Elf32_Sym	*elfsh_copy_symtab(void *addr, int size)
{
  Elf32_Sym	*new;

  XALLOC(new, size * ELFSH_SYMTAB_ENTRY_SIZE, NULL);
  memcpy(new, addr, size * ELFSH_SYMTAB_ENTRY_SIZE);
  return (new);
}





/* Sort the symtab given in parameter */
int			elfsh_sort_symtab(Elf32_Sym *symtab, int size, int type)
{
  Elf32_Sym		tmp;
  int			pass;
  int			index;
  int			sort_param1;
  int			sort_param2;

  if (type != ELFSH_SORT_BY_ADDR && type != ELFSH_SORT_BY_SIZE)
    return (-1);

  for (pass = size; pass > 0; pass--)
    for (index = size - 1; index > 0; index--)
      {

	/* Select the crypteria */
	if (type == ELFSH_SORT_BY_ADDR)
	  {
	    sort_param1 = symtab[index - 1].st_value;
	    sort_param2 = symtab[index].st_value;
	  }
	else if (type == ELFSH_SORT_BY_SIZE)
	  {
	    sort_param1 = symtab[index - 1].st_size;
	    sort_param2 = symtab[index].st_size;
	  }

	if (sort_param1 > sort_param2)
	  {
	    tmp = symtab[index];
	    symtab[index] = symtab[index - 1];
	    symtab[index - 1] = tmp;
	  }

      }

  return (0);
}




/* Return a ptr on the symbol table */
void		*elfsh_get_symtab(Elfsh32_Obj *file, int *num)
{
  Elfsh32_Sect	*s;
  int		strindex;
  int		index;
  int		nbr;

  if (!file->symtab)
    {
      
      s = elfsh_get_section_by_type(file, SHT_SYMTAB, &index, &strindex, &nbr, 0);
      if (!s)
	ELFSH_SETERROR("libelfsh: No symbol table .", NULL);
      
      file->symtab = s;
      file->symtab->nbr = nbr / ELFSH_SYMTAB_ENTRY_SIZE;

      file->symtab->data = elfsh_load_section(file, s->shdr);
      if (!file->symtab->data)
	return (NULL);

      file->symtab->altdata = elfsh_copy_symtab(file->symtab->data, file->symtab->nbr);
      file->symtab->altnbr = file->symtab->nbr;
      elfsh_sort_symtab(file->symtab->altdata, file->symtab->altnbr, ELFSH_SORT_BY_ADDR);

      /* Now load the string table */
      s = elfsh_get_section_by_index(file, strindex, &index, &nbr);
      if (!s)
	return (NULL);

      file->strtab = s;
      s->nbr = nbr;
      s->index = index;
      s->data = elfsh_load_section(file, s->shdr);
      if (!s->data)
	return (NULL);

    }

  if (num)
    *num = file->symtab->nbr;

  return (file->symtab->data);
}






/* Return the dynamic symbol name giving its value, fill the offset with the padding */
char		*elfsh_reverse_symbol(Elfsh32_Obj *file, u_int value, int *offset)
{
  Elfsh32_Sect	*sect;
  Elf32_Sym	*sorted;
  int		num;
  int		index;

  if (value == 0 || value == 0xFFFFFFFF)
    return (NULL);  

  if (!elfsh_get_symtab(file, &num))
    {
      sect = elfsh_get_parent_section(file, value, offset);
      return (elfsh_get_section_name(file, sect->shdr));
    }

  sorted = file->symtab->altdata;
  
  for (index = 0; index < num; index++)
    if (sorted[index].st_value > (u_int) value)
      {
	if (!index)
	  return (NULL);
	*offset = (int) value - sorted[index - 1].st_value;
	return (elfsh_get_symbol_name(file, sorted + index - 1));
      }
    else if (sorted[index].st_value == (u_int) value)
      {
	*offset = 0;
	return (elfsh_get_symbol_name(file, sorted + index));
      }

  return (NULL);

}





/* Return the symbol entry giving its name */
Elf32_Sym	*elfsh_get_symbol_by_name(Elfsh32_Obj *file, char *name)
{
  Elf32_Sym	*ret;
  int		index;
  int		size = 0;
  char		*actual;

  if (!elfsh_get_symtab(file, &size))
    return (NULL);
  if (!name)
    return (NULL);

  ret = file->symtab->data;
  for (index = 0; index < size; index++)
    {
      actual = elfsh_get_symbol_name(file, ret + index);
      if (actual && !strcmp(actual, name))
	return (ret + index);
    }
  
  ELFSH_SETERROR("ELFshlib: symbol not found .", NULL);
}




/* Insert a symbol in the given symbol table */
int		elfsh_insert_symbol(Elfsh32_Sect *sect, Elf32_Sym *sym, char *name)
{
  int		index;

  if (!sect || (sect->shdr->sh_type != SHT_SYMTAB && sect->shdr->sh_type != SHT_DYNSYM))
    ELFSH_SETERROR("libelfsh: Input Section is not a symbol table . \n", -1);
  
  index = elfsh_insert_in_strtab(sect->parent, name);
  if (index < 0)
    return (-1);

  /* Insert symbol name in .strtab */
  sym->st_name = index;
  index = elfsh_append_data_to_section(sect, sym, sizeof(Elf32_Sym));

  /* Resort the symtab */
  sect->nbr++;
  free(sect->altdata);
  sect->altdata = elfsh_copy_symtab(sect->data, sect->nbr);
  sect->altnbr = sect->nbr;
  elfsh_sort_symtab(sect->altdata, sect->altnbr, ELFSH_SORT_BY_ADDR);

  return (index);
}




/* Create a symbol */
Elf32_Sym	*elfsh_create_symbol(int value, int size, int type, int binding, int visibility)
{
  Elf32_Sym	*new;

  XALLOC(new, sizeof(Elf32_Sym), NULL);
  new->st_value = value;
  new->st_size = size;
  new->st_info = ELF32_ST_INFO(binding, type);
  new->st_other = visibility;
  return (new);
}























