/*
** hash.c for libelfsh
** 
** Started on  Mon Feb 26 04:15:44 2001 mayhem
** Last update Mon Mar 25 10:53:51 2002 mayhem
*/
#include "libelfsh.h"



/* Return a pointer on the symbol hash table and load it if needed */
void		*elfsh_get_hashtable(Elfsh32_Obj *file)
{
  Elfsh32_Sect	*new;
  int		nbr;
  
  if (!file->hash)
    {

      new = elfsh_get_section_by_type(file, SHT_HASH, NULL, NULL, &nbr, 0);
      if (!new)
	return (NULL);
      
      new->data = elfsh_load_section(file, new->shdr);
      if (!new->data)	
	ELFSH_SETERROR("libelfsh: No Hash table .", NULL);

      new->nbr = nbr;
      file->hash = new;

    }
  
  return (file->hash);
}






/* Return the symbol value giving its name using the symbol hash table */
int		elfsh_get_dynsymbol_by_hash(Elfsh32_Obj *file, char *name)
{
  Elf32_Sym	*sym;
  char		*sname;
  int		hash;
  int		nbucket;
  int		nchain;
  int		*chain;
  int		*bucket;
  int		index;
  
  if (!elfsh_get_hashtable(file) || !elfsh_get_dynsymtab(file, NULL))
    return (0);
  
  hash    = elfsh_get_symbol_hash(name);
  nbucket = *((int *) file->hash->data);
  nchain  = *((int *) file->hash->data + 1);
  bucket  = (int *) file->hash->data + 2;
  chain   = (int *) bucket + nbucket;
  
  index = bucket[hash % nbucket];
  sym = file->dynsym->data;
  sname = elfsh_get_dynsymbol_name(file, sym + index);

  if (!strcmp(name, sname))
    return (sym[index].st_value);

  /* The first fetched dynamic symbol isnt the one we are looking for, so we loop on the hash */
  for (sym = file->dynsym->data; index < nchain; index++)
    {

#ifdef __DEBUG__
      printf("[LIBELFSH] Hash LOOP on bucket [%u] \n", index);
#endif
      
      sname = elfsh_get_dynsymbol_name(file, &sym[chain[index]]);
      if (!strcmp(name, sname))
	return (sym[chain[index]].st_value);
    }
  
  ELFSH_SETERROR("ELFlib: No Hash found for the symbol .", 0);
}






/* Get the hash for the symbol name . Adapted from the ELF TIS reference */
int		elfsh_get_symbol_hash(char *name)
{
  unsigned long h;
  unsigned long g;
  
  for (h = 0; *name; h &= ~g)
    {
      h = (h << 4) + *name++;
      if ((g = h & 0xF0000000))
	h ^= g >> 24;
    }
  return (h);
}

























