/*
** sht_rebuild.c for elfsh
** 
** Started on  Tue Mar 26 19:07:23 2002 mayhem
** Last update Sun May 12 14:55:19 2002 mayhem
*/
#include "libelfsh.h"



static int	add_sht_entry(Elfsh32_Obj *file, int index, Elf32_Shdr shdr, char *name);
static void	update_name_indexes(Elfsh32_Obj *file, int offset, int len);
static void	update_link_indexes(Elfsh32_Obj *file, int low_index);




/* Recreate the section header table examining the program header table */
int		elfsh_rebuild_sht(Elfsh32_Obj *file)
{
  Elf32_Phdr	*low;
  Elf32_Phdr	*high;
  Elf32_Phdr	*dyn;
  Elf32_Shdr	shdr;
  Elfsh32_Sect	*sect;
  struct stat	st;
  int		num;
  int		index;
  int		flags;

  /* Some preliminary stuffs */
  if (!elfsh_get_hdr(file) || !elfsh_get_pht(file, &num))
    return (-1);

  /* Get the file size on disk */
  if (fstat(file->fd, &st) != 0)
    return (-1);
  file->hdr->e_shentsize = sizeof(Elf32_Shdr);

  /* Do the first pass : guess the borders for .mapped and .unmapped */
  for (low = high = NULL, index = 0; index < num; index++)
    {
      if (!high || (file->pht[index].p_offset + file->pht[index].p_filesz) > (high->p_offset + high->p_filesz))
	high = file->pht + index;
      if (!low || file->pht[index].p_offset < low->p_offset)
	low = file->pht + index;
    }

  /* Create the initial SHT */
  XALLOC(file->sht, file->hdr->e_shentsize * 3, -1);
  file->hdr->e_shoff = st.st_size;
  file->hdr->e_shnum = 3;
  file->hdr->e_shstrndx = 2;

  /* Insert the .mapped section */
  shdr = elfsh_create_section_header(0, SHT_PROGBITS, SHF_ALLOC, low->p_vaddr, low->p_offset, 
				     high->p_offset + high->p_filesz, 0, 0, 0, 0);
  file->sht[0] = shdr;
  XALLOC(sect, sizeof(Elfsh32_Sect), -1);
  if (elfsh_add_section(file, sect, 0, NULL, ELFSH_SHIFTING_PARTIAL) < 0)
    return (-1);

  /* Insert the .unmapped section */
  shdr = elfsh_create_section_header(0, SHT_PROGBITS, 0, 0, high->p_offset + high->p_filesz, 
				     st.st_size - (high->p_offset + high->p_filesz), 0, 0, 0, 0);
  file->sht[1] = shdr;
  XALLOC(sect, sizeof(Elfsh32_Sect), -1);
  if (elfsh_add_section(file, sect, 1, NULL, ELFSH_SHIFTING_PARTIAL) < 0)
    return (-1);

  /* Insert the section string table (.sh_strtab) */
  shdr = elfsh_create_section_header(0, SHT_STRTAB, 0, 0, st.st_size, 0, 0, 0, 0, 0);
  file->sht[2] = shdr;
  XALLOC(sect, sizeof(Elfsh32_Sect), -1);
  if (elfsh_add_section(file, sect, 2, NULL, ELFSH_SHIFTING_PARTIAL) < 0)
    return (-1);
  file->sh_strtab = sect;

  /* Insert names in .strtab */
  file->sht[0].sh_name = elfsh_insert_in_shstrtab(file, ELFSH_SECTION_NAME_MAPPED);
  file->sht[1].sh_name = elfsh_insert_in_shstrtab(file,ELFSH_SECTION_NAME_UNMAPPED);
  file->sht[2].sh_name = elfsh_insert_in_shstrtab(file, ELFSH_SECTION_NAME_SHSTRTAB);

   /* the rest of the code is buggy, remove the return at your own risk :) */
   return (0);
   
   
  /* Iterate on the program header table to reconstruct .dynamic, .interp and .note */
  for (index = 0; index < num; index++)
    {

      /* initialize the flags */
      flags = SHF_ALLOC;
      flags |= (elfsh_segment_is_writable(file->pht + index)    ? SHF_WRITE     : 0);
      flags |= (elfsh_segment_is_executable(file->pht + index)  ? SHF_EXECINSTR : 0);
      
      /* Begin the big switch */
      switch (file->pht[index].p_type)
	{

	  /* Some loadable segment */
	case PT_LOAD:
	  shdr = elfsh_create_section_header(0, SHT_PROGBITS, flags, file->pht[index].p_vaddr, 
					     file->pht[index].p_offset, file->pht[index].p_filesz, 
					     0, 0, 0, 0);
	  if (INTERVAL(file->pht[index].p_vaddr, file->hdr->e_entry, 
		       file->pht[index].p_vaddr + file->pht[index].p_filesz))
	    add_sht_entry(file, index, shdr, ELFSH_SECTION_NAME_TEXT);
	  else
	    add_sht_entry(file, index, shdr, ELFSH_SECTION_NAME_DATA);
	  break;

	  /* The dynamic segment */
	case PT_DYNAMIC:
	  dyn = file->pht + index;
	  shdr = elfsh_create_section_header(0, SHT_DYNAMIC, flags , file->pht[index].p_vaddr,
					     file->pht[index].p_offset, file->pht[index].p_filesz, 
					     0, 0, 0, 0);
	  add_sht_entry(file, index, shdr, ELFSH_SECTION_NAME_DYNAMIC);
	  break;
	  
	  /* The interpretor segment */
	  /*
	case PT_INTERP:
	  shdr = elfsh_create_section_header(0, SHT_PROGBITS, flags, file->pht[index].p_vaddr,
					     file->pht[index].p_offset, file->pht[index].p_filesz, 
					     0, 0, 0, 0);
	  add_sht_entry(file, index, shdr, ELFSH_SECTION_NAME_INTERP);
	  break;
	  */

	  /* This is the auxiliary information */
	  /*
	case PT_NOTE:
	  shdr = elfsh_create_section_header(0, SHT_NOTE, flags, file->pht[index].p_vaddr,
					     file->pht[index].p_offset, file->pht[index].p_filesz, 
					     0, 0, 0, 0);
	  add_sht_entry(file, index, shdr, ELFSH_SECTION_NAME_NOTES_ABI);
	  break;
	  */

	default:
	  break;
	}
    }    
  
  return (0);
}

  


static int		add_sht_entry(Elfsh32_Obj *file, int phdr_index, Elf32_Shdr shdr, char *name)
{
  Elfsh32_Sect		*sect;
  int			index;
  int			namelen;
  int			len;
  int			off;
  char			buff[BUFSIZ];

  namelen = strlen(name);
  printf("Insertion section header *%s* \n", name);

  for (index = 0; index < file->hdr->e_shnum; index++)
    {
      
      /* The new header perfectly match an existing header */
      if (file->sht[index].sh_offset == shdr.sh_offset && file->sht[index].sh_size == shdr.sh_size)
	{

	  /* We are lucky then we optimize a bit */
	  len = (file->sht[index].sh_name ? strlen(file->sh_strtab->data + file->sht[index].sh_name) : 0);
	  if (len == namelen)
	    {
	      printf("SHT rebuild case 1-1 \n");
	      memcpy(file->sh_strtab->data + file->sht[index].sh_name, name, len);
	    }

	  /* The off variable is used just to clean the code a bit . This case is the common one . */
	  else
	    {
	      printf("SHT rebuild case 1-2 \n");
	      off = file->sht[index].sh_name + len + 1;
	      memmove(file->sh_strtab->data + file->sht[index].sh_name, 
		      file->sh_strtab->data + off, 
		      file->sh_strtab->shdr->sh_size - off);
	      file->sht[index].sh_size -= len + 1;
	      update_name_indexes(file, file->sht[index].sh_name, len + 1);
	      file->sht[index].sh_name = elfsh_insert_in_shstrtab(file, name);
	    }
	  break;
	}
      
      /* The new header defines an area matching the beginning of an existing header */
      else if (file->sht[index].sh_offset == shdr.sh_offset)
	{
	  printf("SHT rebuild case 2 \n");
	  XREALLOC(file->sht, file->sht, (file->hdr->e_shnum + 1) * file->hdr->e_shentsize, -1);
	  memmove(file->sht + index + 1, file->sht + index, file->hdr->e_shentsize * (file->hdr->e_shnum - index));

	  file->sht[index] = shdr;
	  file->sht[index + 1].sh_size -= shdr.sh_size;
	  file->sht[index + 1].sh_offset += shdr.sh_size;
	  file->sht[index + 1].sh_addr += shdr.sh_size;

	  if (file->hdr->e_shstrndx >= index)
	    file->hdr->e_shstrndx++;
	  file->sh_strtab->shdr = file->sht + file->hdr->e_shstrndx;
	  update_link_indexes(file, index);
	  file->sht[index].sh_name = elfsh_insert_in_shstrtab(file, name);

	  XALLOC(sect, sizeof(*sect), -1);
	  if (elfsh_add_section(file, sect, index, NULL, ELFSH_SHIFTING_ABSENT) < 0)
	    return (-1);

	  break;
	}
      

      /* The new header defines an area matching the end of an existing header */
      else if (file->sht[index].sh_offset + file->sht[index].sh_size == shdr.sh_offset + shdr.sh_size)
	{
	  printf("SHT rebuild case 3 \n");
	  XREALLOC(file->sht, file->sht, (file->hdr->e_shnum + 1) * file->hdr->e_shentsize, -1);
	  memmove(file->sht + index + 2, file->sht + index + 1, 
		  file->hdr->e_shentsize * (file->hdr->e_shnum - index - 1));

	  file->sht[index + 1] = shdr;
	  file->sht[index].sh_size -= shdr.sh_size;

	  file->hdr->e_shstrndx++;
	  file->sh_strtab->shdr = file->sht + file->hdr->e_shstrndx;
	  update_link_indexes(file, index + 1);
	  file->sht[index + 1].sh_name = elfsh_insert_in_shstrtab(file, name);
	  
	  XALLOC(sect, sizeof(*sect), -1);
	  if (elfsh_add_section(file, sect, index, NULL, ELFSH_SHIFTING_ABSENT) < 0)
	    return (-1);
	  break;
	}
      

      /* The new header defines an area strictly included in an existing section */
      else if (file->sht[index].sh_offset < shdr.sh_offset && 
	       file->sht[index].sh_offset + file->sht[index].sh_size > shdr.sh_offset + shdr.sh_size)
	{
	  printf("SHT rebuild case 4 \n");
	  XREALLOC(file->sht, file->sht, (file->hdr->e_shnum + 2) * file->hdr->e_shentsize, -1);
	  memmove(file->sht + index + 3, file->sht + index + 1, file->hdr->e_shentsize * (file->hdr->e_shnum - index));

	  file->sht[index + 1] = shdr;
	  file->sht[index + 2] = elfsh_create_section_header(0, file->sht[index].sh_type,
							     file->sht[index].sh_flags,
							     shdr.sh_addr + shdr.sh_size,
							     shdr.sh_offset + shdr.sh_size,
							     file->sht[index].sh_size - 
							     (shdr.sh_offset - file->sht[index].sh_offset) - 
							     shdr.sh_size,
							     0, 0, 0, 0);
	  file->sht[index].sh_size = shdr.sh_offset - file->sht[index].sh_offset;

	  file->hdr->e_shstrndx += 2;
	  update_link_indexes(file, index);
	  file->sh_strtab->shdr = file->sht + file->hdr->e_shstrndx;
	  shdr.sh_name = elfsh_insert_in_shstrtab(file, name);
	  snprintf(buff, sizeof(buff), "%s_%u", 
		   (file->sht[index + 2].sh_addr ? ELFSH_SECTION_NAME_MAPPED : ELFSH_SECTION_NAME_UNMAPPED),
		   file->sht[index + 2].sh_offset);
	  file->sht[index + 2].sh_name = elfsh_insert_in_shstrtab(file, buff);

	  XALLOC(sect, sizeof(*sect), -1);
	  if (elfsh_add_section(file, sect, index, NULL, ELFSH_SHIFTING_ABSENT) < 0)
	    return (-1);
	  XALLOC(sect, sizeof(*sect), -1);
	  if (elfsh_add_section(file, sect, index + 2, NULL, ELFSH_SHIFTING_ABSENT) < 0)
	    return (-1);

	  break;
	}
    }       

  return (0);
}




/* Update the sh_name field in the section header table entry */
/* INTERNAL FUNCTION used by elfsh_rebuild_sht() */
static void	update_name_indexes(Elfsh32_Obj *file, int offset, int len)
{
  int		index;

  for (index = 0; index < file->hdr->e_shnum; index++)
    if (file->sht[index].sh_name > offset)
      file->sht[index].sh_name -= len;
}




/* Update the sh_index field in the section header table entry */
/* INTERNAL FUNCTION used by elfsh_rebuild_sht() */
static void	update_link_indexes(Elfsh32_Obj *file, int low_index)
{
  Elfsh32_Sect	*current;
  int		index;

  for (index = 0, current = file->sections; index < file->hdr->e_shnum; index++, current = current->next)
    {
      current->shdr = file->sht + index;
      if (file->sht[index].sh_link >= low_index)
	file->sht[index].sh_link++;
    }
}
























