/* 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; }