/* Test Valgrind's ability to spot writes to code which has been
   translated, and discard the out-of-date translations.

   CORRECT output is

      in p 0
      in q 1
      in p 2
      in q 3
      in p 4
      in q 5
      in p 6
      in q 7
      in p 8
      in q 9

  WRONG output (if you fail to spot code-writes to code[0 .. 4]) is

      in p 0
      in p 1
      in p 2
      in p 3
      in p 4
      in p 5
      in p 6
      in p 7
      in p 8
      in p 9
*/

#include <stdio.h>
#include <assert.h>
#include "tests/sys_mman.h"

typedef unsigned long long int Addr;
typedef unsigned char UChar;

void q ( int n )
{
   printf("in q %d\n", n);
}

void p ( int n )
{
   printf("in p %d\n", n);
}

// Unlike on x86, data areas aren't executable; have to put
// code on the heap therefore
static UChar* code;

/* Make `code' be  movabsq $dest, %rax ; pushq %rax ; ret */
// This forces the branch onwards to be indirect, so vex can't chase it
void set_dest ( Addr dest )
{
   assert(sizeof(Addr) == 8);

   /* movabsq $imm64, %rax */
   code[0] = 0x48;
   code[1] = 0xB8;
   code[2] = (dest & 0xFF);
   code[3] = ((dest >>  8) & 0xFF);
   code[4] = ((dest >> 16) & 0xFF);
   code[5] = ((dest >> 24) & 0xFF);
   code[6] = ((dest >> 32) & 0xFF);
   code[7] = ((dest >> 40) & 0xFF);
   code[8] = ((dest >> 48) & 0xFF);
   code[9] = ((dest >> 56) & 0xFF);

   /* pushq %rax */
   code[10] = 0x50;

   /* ret */
   code[11] = 0xC3;
}

/* Calling aa gets eventually to the function residing in code[0..].
   This indirection is necessary to defeat Vex's basic-block chasing
   optimisation.  That will merge up to three basic blocks into the
   same IR superblock, which causes the test to succeed when it
   shouldn't if main calls code[] directly.  */

// force an indirect branch to code[0], so vex can't chase it
__attribute__((noinline))
void dd ( int x, void (*f)(int) ) { f(x); }

__attribute__((noinline))
void cc ( int x ) { dd(x, (void(*)(int)) &code[0]); }

__attribute__((noinline))
void bb ( int x ) { cc(x); }

__attribute__((noinline))
void aa ( int x ) { bb(x); }

__attribute__((noinline))
void diversion ( void ) { }

int main ( void )
{
   int i;
   code = mmap(NULL, 20, PROT_READ|PROT_WRITE|PROT_EXEC,
               MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
   assert(code != MAP_FAILED);
   for (i = 0; i < 10; i += 2) {
      set_dest ( (Addr)&p );
      //      diversion();
      aa(i);
      set_dest ( (Addr)&q );
      //      diversion();
      aa(i+1);
   }
   munmap(code, 20);
   return 0;
}