#include <stdio.h>
#include <stdlib.h>
/* #include <unistd.h> */
/* #include <sys/mman.h> */
/* #include <errno.h> */
/* #include <string.h> */

#define SELF_CODE_MODIFYING      1
#define SELF_CODE_MODIFYING_ADDR 1
// 0 : with label in function
// 1 : fixed


#if SELF_CODE_MODIFYING
static int  self_code_modifying_nb_runs;
extern char self_code_modifying_function_destination_start;
extern char self_code_modifying_function_destination_end;
extern char self_code_modifying_function_source_start;
extern char self_code_modifying_function_source_end;

int  self_code_modifying_manage              (int nb_runs);
void self_code_modifying_function_source     (void);
void self_code_modifying_function_destination(void);

int self_code_modifying(int nb_runs)
{
 return self_code_modifying_manage(nb_runs);
}

int  self_code_modifying_manage(int nb_runs)
{
  
//  // Change the memory protection
//  unsigned page = (unsigned)&self_code_modifying_function_destination_start & ~( getpagesize() - 1 );
//  
//  if( mprotect( (char*)page, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC ) )
//    {
//      perror( "mprotect failed" );
//      exit( errno );
//    }

  // First execution
  self_code_modifying_function_destination();


#if   (SELF_CODE_MODIFYING_ADDR==0)
  int function_source_start      = (int)&self_code_modifying_function_source_start     ;
  int function_source_end        = (int)&self_code_modifying_function_source_end       ;
  int function_destination_start = (int)&self_code_modifying_function_destination_start;
  int function_destination_end   = (int)&self_code_modifying_function_destination_end  ;
#elif (SELF_CODE_MODIFYING_ADDR==1)
  int function_source_start      = 0x00400330;
  int function_source_end        = 0x00400358;
  int function_destination_start = 0x0040036c;
  int function_destination_end   = 0x004004d8;
#else
  #error "Invalid SELF_CODE_MODIFYING_ADDR."
#endif

  int size_source      = function_source_end     -function_source_start;
  int size_destination = function_destination_end-function_destination_start;
  
  printf(" * self_code_modifying_function_destination : %x to %x (%d bytes)\n",function_destination_start,function_destination_end,size_destination);
  printf(" * self_code_modifying_function_source      : %x to %x (%d bytes)\n",function_source_start     ,function_source_end     ,size_source);
  
  if (size_destination<size_source)
    {
      printf("Size of destination is too short\n");
      return 1;
    }
  
  printf(" * memcpy\n");
  memcpy((void*)function_destination_start, (void*)function_source_start, function_source_end - function_source_start );
  
  while (self_code_modifying_nb_runs<nb_runs)
    {
      self_code_modifying_function_destination();
    }

  return 0;
}

void self_code_modifying_function_source(void)
{
  int  (*callPrintf  )(const char *format, ...); 
 
  asm volatile( "self_code_modifying_function_source_start:" );
  
  (*(callPrintf=&printf))( " + self_code_modifying_function_source\n" );
  self_code_modifying_nb_runs++;
  (*(callPrintf=&printf))( " + Number of call : %d\n",self_code_modifying_nb_runs);
  
  asm volatile( "self_code_modifying_function_source_end:");
}

void self_code_modifying_function_destination(void)
{
  int  (*callPrintf  )(const char *format, ...); 

  asm volatile( "self_code_modifying_function_destination_start:");

  (*(callPrintf=&printf))( " + self_code_modifying_function_destination\n" );
  
  // Padding
  asm volatile (//".set push        \n\t"
                //".set noreorder   \n\t"
                //".set noat        \n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t"
                "nop; nop; nop; nop; nop; nop; nop; nop;\n\t");
    
  asm volatile( "self_code_modifying_function_destination_end:");
}

#else

int self_code_modifying(int nb_runs)
{
  return 0;
}

#endif
