/*
** sht.c for libelfsh
** 
** Started on  Mon Feb 26 04:16:18 2001 mayhem
** Last update Wed Apr  3 00:03:17 2002 mayhem
*/
#include "libelfsh.h"





/* Return a ptr on the section header table */
void		*elfsh_get_sht(Elfsh32_Obj *file, int *num)
{
  Elfsh32_Sect	*s;
  char		*name;

  /* Load the section header table and allocate every Elfsh32_Sect */
  if (file->sht)
    {
      if (num)
	*num = file->hdr->e_shnum;
      return (file->sht);
    }
  else if (!file->sht && elfsh_load_sht(file) < 0)
    if (elfsh_rebuild_sht(file) < 0)
      ELFSH_SETERROR("elfsh_get_sht: Cant load sht .", NULL);
  
  /* Now load the section string table if not already done in elfsh_rebuild_sht() */
  if (!file->sh_strtab)
    {
      s = elfsh_get_section_by_index(file, file->hdr->e_shstrndx, NULL, NULL);
      if (!s)
	ELFSH_SETERROR("elfsh_get_sht: Cant read .shstrtab header .", NULL);
      file->sh_strtab = s;
      s->data = elfsh_load_section(file, s->shdr);
      if (!s->data)
	ELFSH_SETERROR("elfsh_get_sht: Cant read .shstrtab in the ELF file .", NULL);
      s->nbr = s->shdr->sh_size;
    }

  /* Now associate sections with section names . Note: We cant do that in the main loop in elfsh_load_sht() 
     since the string table was not yet loaded at this time */
  for (s = file->sections; s; s = s->next)
    {
      name = elfsh_get_section_name(file, s->shdr);
      s->name = (name ? strdup(name) : NULL);
    }

  /* Some final stuffs */
  if (num)
    *num = file->hdr->e_shnum;
  return (file->sht);

}







/* Return an array of section header */
int		elfsh_load_sht(Elfsh32_Obj *file)
{
  Elfsh32_Sect	*new;
  int		size;
  int		index;

  if (!file->hdr && !elfsh_get_hdr(file))
    return (-1);
  else if (file->sht)
    return (0);
  
  size = file->hdr->e_shnum * file->hdr->e_shentsize;

  if (!file->hdr->e_shoff)
    return (-1);

  XSEEK(file->fd, file->hdr->e_shoff, SEEK_SET, -1);
  XALLOC(file->sht, size, -1);
  XREAD(file->fd, file->sht, size, -1);

  /* Allocate a Elfsh32_Sect per section and put it in the file->sections double linked list */
  for (index = 0; index < file->hdr->e_shnum; index++)
    {
      XALLOC(new, sizeof(Elfsh32_Sect), -1);
      if (elfsh_add_section(file, new, index, NULL, ELFSH_SHIFTING_COMPLETE) < 0)
	return (-1);
    }

  return (0);
}







/* Retreive the section header giving the section symbol from .symtab */
Elf32_Shdr		*elfsh_get_shtentry_from_sym(Elfsh32_Obj *file, Elf32_Sym *sym)
{
  Elf32_Shdr		*current;
  int			index;
  int			nbr;

  if (!elfsh_get_sht(file, &nbr))
    return (NULL);

  for (index = 0, current = file->sht; index < nbr; index++)
    if (current[index].sh_addr == sym->st_value)
      return (current + index);

  return (NULL);
}







/* Create a section header */
Elf32_Shdr		elfsh_create_section_header(Elf32_Word name, Elf32_Word type, Elf32_Word flags,
						    Elf32_Addr addr, Elf32_Off offset, Elf32_Word size,
						    Elf32_Word link, Elf32_Word info, Elf32_Word align,
						    Elf32_Word entsize)
						  
{
  Elf32_Shdr		new;

  new.sh_name = name;
  new.sh_type = type;
  new.sh_flags = flags;
  new.sh_addr = addr;
  new.sh_offset = offset;
  new.sh_size = size;
  new.sh_link = link;
  new.sh_info = info;
  new.sh_addralign = align;
  new.sh_entsize = entsize;
  return (new);
}






/* Add a section header to the object */
int		elfsh_insert_section_header(Elfsh32_Obj *file, Elf32_Shdr hdr, u_int range, char *name)
{
  Elfsh32_Sect	*tmp;
  int		index;

  /* Preliminary checks */
  if (!file || !elfsh_get_hdr(file) || (file->hdr->e_shoff && !elfsh_get_sht(file, NULL)))
    return (-1);

  /* Fixup the range */
  if (range == ELFSH_SECTION_LAST)
    range = file->hdr->e_shnum;
  else if (range > file->hdr->e_shnum)
    ELFSH_SETERROR("libelfsh: Invalid range for section injection . \n", -1);

  /* If we have an influence on the sht file offset, update it */
  if (hdr.sh_offset < file->hdr->e_shoff)
    file->hdr->e_shoff += hdr.sh_size;
  
  /* Insert the name string in .shstrtab */
  hdr.sh_name = elfsh_insert_in_shstrtab(file, name);
  if (hdr.sh_name < 0)
    ELFSH_SETERROR("libelfsh: Cant insert data in .shstrtab for section injection . \n", -1);
  if (range > file->sh_strtab->index)
    hdr.sh_offset += strlen(name) + 1;


  /* Extend the SHT and fill the new slot */
  XREALLOC(file->sht, file->sht, file->hdr->e_shentsize * (file->hdr->e_shnum + 1), -1);
  if (range != file->hdr->e_shnum)
    memmove(file->sht + range + 1, file->sht + range, (file->hdr->e_shnum - range) * file->hdr->e_shentsize);
  file->sht[range] = hdr;
  file->hdr->e_shnum++;

  /* Update the file offset for all section header whoose section is placed after the sht */
  for (tmp = file->sections, index = 0; index < file->hdr->e_shnum; index++)
    {

      /* Since we reallocated the sht, we have to update the shdr ptr since its pointing to the old chunk ! */
      /* Pass headers without body (this include the just inserted one) */
      if (index != range && tmp)
	{
	  tmp->shdr = file->sht + index;
	  tmp = tmp->next;
	}
      
      /* Now update the file offset for the actual section by sizeof(Elf32_Shdr) */
      if (file->sht[index].sh_offset >= file->hdr->e_shoff)
	file->sht[index].sh_offset += file->hdr->e_shentsize;

    }
  
  
  /* Update the .shstrtab index in the ELF header if necessary */
  if (range <= file->hdr->e_shstrndx)
    file->hdr->e_shstrndx++;

  return (0);
}




/* get the section's name in .shstrtab */
char		*elfsh_get_section_name(Elfsh32_Obj *file, Elf32_Shdr *s)
{
  char		*str;

  if (!s || !file || !elfsh_get_sht(file, NULL))
    return (NULL);
  str = file->sh_strtab->data;
  return (str + s->sh_name);
}



/* Tell elfsh that we dont want the sht in the output file */
void		elfsh_remove_sht(Elfsh32_Obj *file)
{
  file->remove_sht_on_exit = 1;
}





/* All next functions are dumb field access routines */
u_int		elfsh_get_section_info(Elf32_Shdr *s)
{
  return (s->sh_info);
}

void		elfsh_set_section_info(Elf32_Shdr *s, Elf32_Word info)
{
  s->sh_info = info;
}

u_int		elfsh_get_section_entsize(Elf32_Shdr *s)
{
  return (s->sh_entsize);
}

void		elfsh_set_section_entsize(Elf32_Shdr *s, Elf32_Word entsize)
{
  s->sh_entsize = entsize;
}

u_int		elfsh_get_section_link(Elf32_Shdr *s)
{
  return (s->sh_link);
}

void		elfsh_set_section_link(Elf32_Shdr *s, Elf32_Word link)
{
  s->sh_link = link;
}

int	elfsh_section_is_allocatable(Elf32_Shdr *s)
{
  return ((s->sh_flags & SHF_ALLOC));
}

int	elfsh_section_is_writable(Elf32_Shdr *s)
{
  return ((s->sh_flags & SHF_WRITE));
}

int	elfsh_section_is_executable(Elf32_Shdr *s)
{
  return ((s->sh_flags & SHF_EXECINSTR));
}

int	elfsh_section_is_mergeable(Elf32_Shdr *s)
{
  return ((s->sh_flags & SHF_MERGE));
}

int	elfsh_section_is_strtab(Elf32_Shdr *s)
{
  return ((s->sh_flags & SHF_STRINGS));
}

int	elfsh_section_has_link(Elf32_Shdr *s)
{
  return ((s->sh_flags & SHF_INFO_LINK));
}

int	elfsh_section_has_order(Elf32_Shdr *s)
{
  return ((s->sh_flags & SHF_LINK_ORDER));
}

int	elfsh_get_section_foffset(Elf32_Shdr *s)
{
  return (s->sh_offset);
}

void	elfsh_set_section_foffset(Elf32_Shdr *s, Elf32_Off offset)
{
  s->sh_offset = offset;
}

int	elfsh_get_section_vaddr(Elf32_Shdr *s)
{
  return (s->sh_addr);
}

void	elfsh_set_section_addr(Elf32_Shdr *s, Elf32_Addr addr)
{
  s->sh_addr = addr;
}

int	elfsh_get_section_align(Elf32_Shdr *s)
{
  return (s->sh_addralign);
}

int	elfsh_get_section_size(Elf32_Shdr *s)
{
  return (s->sh_size);
}

u_int	elfsh_get_section_type(Elf32_Shdr *s)
{
  return (s->sh_type);
}








