#include <stdio.h>
#include "insertor.h"
#include "utils.h"

int is_good_char(char c)
{
  if (c >= ' ' && c <= '~')
    return 1;
  if (c == '\n' || c == '\t')
    return 1;
  return 0;
}

/* return the size of relocation for offset
   0 if no entry */
int
is_reloc(u_int offset)
{
 int i;
 for (i = 0; i < treloc_len; i++)
   {
     if (treloc_table[i].r_address == offset)
       return 1 << treloc_table[i].r_length;
     if (treloc_table[i].r_address > offset)
       return 0;	/* sorted */
   }
 return 0;
}

void
search_data()
{
 int i,j,k;
 int state = 0;
 for (i = 0; i < text_len; i++ )
   {
     if ( (k=is_reloc(i)) != 0)
       {
         state = 0;
         i += k -1;
         continue;
       }
     if (state == 0)
       {
         if (is_good_char(text_seg[i]))
           {
             state = 1;
             j = i;
             continue;
           }
       }
     else
       {
         if (text_seg[i] == '\0' && (i - j) > 3)
           {
             for (; j <= i; j++)
               text_bm[j] |= TBM_LIT;
             state = 0;
           }
         if (!is_good_char(text_seg[i]))
           state = 0;
       }
   }
}
           
/* return the size of relocation for offset
   0 if no entry */
struct relocation_info*
search_reloc(u_int offset)
{
 int i;
 for (i = 0; i < treloc_len; i++)
   {
     if (treloc_table[i].r_address == offset)
       {
         if (i < (treloc_len - 1) && treloc_table[i+1].r_address == offset)
           FATAL(1,"There are two reloc info at least!\n");
         treloc_table[i].r_pad = 1;
         return &treloc_table[i];
       }
     if (treloc_table[i].r_address > offset)
       return (struct relocation_info*)0;	/* sorted */
   }
 return (struct relocation_info*)0;
}

/* test if it is data.
 * Should return 1 if we are sure there are data.
 */
int
is_static_data(struct TSym *current)
{
 int len;
 int state = 0;
 int i;
 u_char *l = symbols[current->sym].n_un.n_name;
 
 len = strlen(l);
 if (len < 2)
   return -1;	/* bad label */
   
 /* First we test the label.
  * GCC creates label for static data (in a function) with this shape:
  *        _xxx.nnn		where n are numbers 
  */  
 /* This is an determinist 'tree'
  * at the beginning state is 0 
  *  state |  '.'  | '0'-'9' |
  *  ------+-------+---------+
  *   0    |   2   |    0    |
  *   1    |   1   |    1    |
  *   2    |   1   |    3    |
  *   3    |   1   |    3    |
  */
 for (i = 0; i < len; i++)
   {
     if (state == 0 && l[i] == '.')
       {
         state = 2;
         continue;
       }
     if (state > 1 && l[i] >= '0' && l[i] <= '9')
       {
         state = 3;
         continue;
       }
     else
       if (state == 3)
          {
            state = 1;
            continue;
          }
   }
 if (state == 3)
   return 1;
 /* test if the block beginns with relocation. */
 if (is_reloc(current->addr) != 0)
   return 1;
 /* test for usual first instructions */
 l = &text_seg[current->addr];
 if (    (l[0] == 0x55)				/* pushl %ebp */
      || (l[0] == 0x83 && l[1] == 0xec))	/* subl %esp, xx */
   return 0;
 /* test for NULL */
 if (l[0] == 0 && l[1] == 0)
   return 1;
 if (current->addr % func_align != 0)
   return 1;	/* functions are aligned */
 /* We don't know. */
  return 0;
}

void
gap(u_int off1, u_int off2)
{
 u_int i,j;
 struct relocation_info *ri;
 
 for (i = off1; i < off2; i++)
   if (text_seg[i] != NOP_OPCODE)
     {
       printf("I think there are data between 0x%x and 0x%x\n", off1, off2);
       for(ri = treloc_table, j = 0; j < treloc_len; j++, ri++)
         {
           if (ri->r_address < off1)
             continue;
           if (ri->r_address >= off2)
             return;
           ri->r_pad = 1;
         }
       return;
     }
 printf("There are only 'nop' between 0x%x and 0x%x\n", off1, off2);
}

int
is_rupture(u_int addr)
{
 if (text_seg[addr] == 0xc2 || text_seg[addr] == 0xc3)
   return 1;	/* ret */
 if (text_seg[addr] == 0xeb || text_seg[addr] == 0xe9)
   return 1;	/* jmp */
 if (text_seg[addr] == 0xff && (text_seg[addr+1] & 0x31) == 0x20)
   return 1;
 return 0;
}

void
director()
{
  u_int offset = 0;
  u_int i,j;
  struct dis_info info;
  int state = 0;
  
  for (i = 0; i < text_len;)
    {
      offset = i;
      again:
      switch(state)
        {
          case 0:
            printf("I am looking for an entry point\n");
            if (text_bm[i] & TBM_SYM)
              {
                printf("Found an entry\n");
                state = 1;
                save_text_bm();
                goto again;
              }
            i++;
            break;
          case 1:
            printf("Try to disassemble\n");
            j = is_reloc(offset);
            if (j != 0)
              {
                printf("There are %d data\n", j);
                restore_text_bm();
                state = 0;
                break;
              }
            info.offset = offset;
            disassemble(&info);
            dis_res = info.length;
            if (info.length == 0)
              {
                printf("This looks like data...\n");
                restore_text_bm();
                state = 0;
                break;
              }
            text_bm[i] |= TBM_ENTRY;
            text_decoded++;
            if (info.type == T_RET || info.type == T_JUMP)
              state = 0;
            for (i++, dis_res-- ; dis_res > 0; dis_res--)
              {
                text_bm[i++] |= TBM_INSTR;
                text_decoded++;
              }
            break;
        }      
    }
}

#if 0
void
director()
{
  u_int offset = 0;
  u_int next_offset;
  int i,j;
  struct TSym *current;
  
  for (i = 0; i < nbr_text_sym; i++)
    {
      current = &text_sym[i];
      if (current->addr > offset)
        gap(offset, current->addr);
      printf("%s:\n",symbols[current->sym].n_un.n_name);
      offset = current->addr;
      /* the end */
      next_offset = (i == (nbr_text_sym - 1)) ? text_len : text_sym[i+1].addr;
      if (current->gcc && is_static_data(current) == 1)
        {
          printf("This looks like as static data. Skipf\n");
          continue;
        }
      do
        {
          j = is_reloc(offset);
          if (j != 0)
            {
              printf("Skipf %d bytes: there are data\n", j);
              search_reloc(offset);
              offset += j;
            }
          else
            {
              info.offset = offset;
              disassemble(&info);
              dis_res = info.length;
              if (info.length == 0)
                {
                  printf("This looks like data...Skipf them.\n");
                  break;
                }
              offset += info.length;
            }
        }
      while (!is_rupture(offset) && offset < next_offset);
      if (is_rupture(offset))
        {
          text_bm[offset] |= TBM_ENDING;
          info.offset = offset;
          disassemble(&info);
          offset += info.length;
        }
    }
}
#endif

int
main(int argc, char *argv[])
{
 int i,n;
/*  int off = 0; */
  if(argc != 2)
    {
      fatal(1,"usage: %s prog\n",argv[0]);
      exit(1);
    }
  if (!load_symbol_table(argv[1]))
    fatal(1,"Can't load symbol table\n");
  init_instr_table();
  init_sym_gen();
  init_text_bm();
  first_process();
  printf("Search data...\n");
  search_data();
  director();
  /* search unused text reloc entry */
  n = 0;
  for (i = 0; i < treloc_len; i++)
    if (treloc_table[i].r_pad == 0)
      n++;
  if (n != 0)
    printf("There are %d / %d unused text reloc entry!\n", n, treloc_len);
  show_text_bm();
  printf("There are %d / %d bytes decoded\n", text_decoded, text_len);
  return 0;
}
