/*
** functions.c for libelfsh
** 
** Started on  Sun Jul 22 05:02:47 2001 mayhem
** Last update Sun Mar 24 07:30:14 2002 mayhem
*/
#include "libelfsh.h"








/* 
** Parse the symbol table looking for non-zero-lenght-functions symbol and add it 
** to the corresponding parent Elfsh32_Sect object .
*/
int			elfsh_scan_functions(Elfsh32_Obj *file) 
{
  Elfsh32_Func		*fct;
  Elfsh32_Func		*prev_fct;
  Elf32_Sym		*sym;
  int			index;
  char			is_plt_entry;


  /* Some preliminary checks */
  if (file->scanned)
    return (0);
  if (!elfsh_get_symtab(file, NULL))
    return (-1);
  if (!elfsh_get_plt(file, NULL))
    return (-1);
  

  /* Entering the symtab loop ... */
  for (prev_fct = fct = NULL, index = 0; index < file->symtab->nbr; index++)
    {

      sym = (Elf32_Sym *) file->symtab->data + index;

      if (elfsh_get_symbol_type(sym) == STT_FUNC)
	{
	  
	  /* Load the corresponding section if requested */
	  XALLOC(fct, sizeof(Elfsh32_Func), -1);
	  if ((fct->section = elfsh_get_parent_section(file, sym->st_value, NULL)) == NULL)
	    return (-1);
	  if (!fct->section->data)
	    elfsh_get_anonymous_section(file, fct->section);

	  /* Create the Elfsh32_Func entry */
	  is_plt_entry = elfsh_is_pltentry(file, sym);
	  fct->sym = sym;
	  fct->offset = sym->st_value - file->sections->shdr->sh_addr;
	  
	  /* Fill the lenght field for the ELfsh32_Func */
	  if (is_plt_entry)
	    fct->len = fct->sym->st_size = ELFSH_PLT_ENTRY_SIZE;
	  else 
	    fct->len = file->sections->shdr->sh_addr + file->sections->shdr->sh_size - sym->st_value;
	  
	  /* Add the Elfsh32_Func at the end of the list for this section */
	  if (prev_fct)
	    prev_fct->len -= (is_plt_entry ? 0 : fct->len);
	  elfsh_insert_function(fct->section, fct, ELFSH_FUNCTION_FIRST);
	  prev_fct = fct;

	}
      
    }

  file->scanned = 1;
  if (elfsh_sort_functions(file) < 0)
    return (-1);

  /* Everything is okay ! yeah man $B> */
  return (0);
  
}    










/*
** Sort the Elfsh32_Func array for each executable section in the file .
** -> Sort the section's functions list by their address from lower to higher
** The algorithm is bubble sort like . This is in tht.c because its a static
** function and its called only one in elf_get_tht() in this file .
*/
int			elfsh_sort_functions(Elfsh32_Obj *file)
{
  Elfsh32_Func		tmp;
  Elfsh32_Func		*fct;	
  Elfsh32_Sect		*s;
  int			pass;

  if (!file->scanned && elfsh_scan_functions(file) < 0)
    return (-1);
  
  for (s = file->sections; s; s = s->next)
    if (elfsh_section_is_executable(s->shdr))
      {

	/* Lame bubble sort */
	for (pass = s->nbr; pass; pass--)
	  for (fct = s->altdata; fct && fct->next; fct = fct->next)
	    if (fct->sym->st_value > fct->next->sym->st_value)
	      {
		
		tmp.sym = fct->sym;
		tmp.section = fct->section;
		tmp.offset = fct->offset;
		tmp.len = fct->len;
		
		fct->sym = fct->next->sym;
		fct->section = fct->next->section;
		fct->offset = fct->next->offset;
		fct->len = fct->next->len;
		
		fct->next->sym = tmp.sym;
		fct->next->section = tmp.section;
		fct->next->offset = tmp.offset;
		fct->next->len = tmp.len;
	      }
	
      }
  
  return (0);

}






/* Free all function entries for all executable files */
void		elfsh_free_functions(Elfsh32_Obj *file)
{
  Elfsh32_Sect	*sct;
  Elfsh32_Sect	*tmpsct;
  Elfsh32_Func	*fct;
  Elfsh32_Func	*tmpfct;

  /* to shut gcc up */
  tmpsct = NULL;
  tmpfct = NULL;

  for (sct = file->sections; sct; sct = tmpsct)
    if (elfsh_section_is_executable(sct->shdr))
      {

	tmpsct = sct->next;
	for (fct = sct->altdata; fct; fct = tmpfct)
	  {
	    tmpfct = fct->next;
	    free(fct);
	  }
	free(sct);
      }

}
    






/* Return the Elfsh32_Func giving its address */
Elfsh32_Func	*elfsh_get_function_by_addr(Elfsh32_Obj *file, int addr)
{
  Elfsh32_Sect	*sct;
  Elfsh32_Func	*fct;
  
  if (!elfsh_scan_functions(file))
    return (NULL);

  for (sct = file->sections; sct; sct = sct->next)
    for (fct = sct->data; fct; fct = fct->next)
      if (fct->sym->st_value == addr)
	return (fct);

  ELFSH_SETERROR("libelfsh: No function at this address", NULL);
}






/* Return the Elfsh32_Func whoose address match the 'name' symbol one */
Elfsh32_Func	*elfsh_get_function_by_name(Elfsh32_Obj *file, char *name)
{
  Elf32_Sym	*sym;
  u_int		type;

  if (!(sym = elfsh_get_symbol_by_name(file, name)))
    ELFSH_SETERROR("libelfsh: No function by that name", NULL);

  type = elfsh_get_symbol_type(sym);

  if (type == STT_FUNC)
    ELFSH_SETERROR("libelfsh: symbol is not a function", NULL);
  
  return (elfsh_get_function_by_addr(file, sym->st_value));
}





/* Return the function lenght */
int	elfsh_get_function_len(Elfsh32_Func *func)
{
  return (func->sym->st_size);
}




/* Return the function address */
int	elfsh_get_function_addr(Elfsh32_Func *func)
{
  return (func->sym->st_value);
}





/* Insert a function to the section */
int		elfsh_insert_function(Elfsh32_Sect *sct, Elfsh32_Func *fct, int index)
{
  Elfsh32_Func	*tmp;

  if (index == ELFSH_FUNCTION_FIRST)
    {
      fct->next = sct->altdata;
      sct->altdata = fct;
    }
  else if (index == ELFSH_FUNCTION_LAST)
    {
      if (!sct->altdata)
	sct->altdata = fct;
      else
	{
	  tmp = sct->altdata;
	  while (tmp && tmp->next)
	    tmp = tmp->next;
	  tmp->next = fct;
	}
    }
  sct->altnbr++;
  return (0);
}




















