#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <signal.h>
#include <string.h>
#include <setjmp.h>
#include <assert.h>
#include <sys/mman.h>

#define MAGIC 0xfeedbeef
#define PAGE_SIZE getpagesize()
#define ALIGN(X,A) ((X) - (((unsigned long) (X)) % (A)))

unsigned char load_asm[] = {
  /* 080483c4 <plain_c>: */
  /*  80483c4:       55                      push   %ebp */
  /*  80483c5:       89 e5                   mov    %esp,%ebp */
  /*  80483c7:       b8 ef be ed fe          mov    $0xfeedbeef,%eax */
  /*  80483cc:       5d                      pop    %ebp */
  /*  80483cd:       c3                      ret    */

  /* Really we just need the mov and ret. Saving %ebp is unnecessary
     since we're not going to change it. */
  0xb8, 0xef, 0xbe, 0xed, 0xfe,                 /* mov */
  0xc3                                          /* ret */
};

int plain_c()
{
  return MAGIC;
}

int plain_asm()
{
  volatile int result;
  __asm__("movl %1, %0" : "=r" (result) : "i" (MAGIC));
  return result;
}

sigjmp_buf segv_env;
struct sigaction old_segv;

void sig_segv(int sig)
{
  siglongjmp(segv_env, 1);
}

int segv_guard()
{
  struct sigaction our_segv;
  int seen_segv = 0;

  our_segv.sa_handler = sig_segv;
  our_segv.sa_flags = 0;
  sigemptyset(&our_segv.sa_mask);

  sigaction(SIGSEGV, &our_segv, &old_segv);
  seen_segv = sigsetjmp(segv_env, 1);

  if (seen_segv) {
    sigaction(SIGSEGV, &old_segv, NULL);
    return 1;
  }

  return 0;
}

void segv_unguard()
{
  sigaction(SIGSEGV, &old_segv, NULL);
}

int call_mem(void *mem)
{
  register int *eax __asm("%eax");
  int result;

  if (segv_guard())
    return 0;

  __asm__("call *%1" : "=r" (eax) : "g"(mem));
  result = (int) eax;

  segv_unguard();

  return result;
}

int indirect_c()
{
  return call_mem(plain_c);
}

int indirect_asm()
{
  return call_mem(plain_asm);
}

int asm_text()
{
  mprotect(ALIGN(load_asm, PAGE_SIZE), PAGE_SIZE,
           PROT_READ|PROT_WRITE|PROT_EXEC);
  return call_mem(load_asm);
}

int asm_stack()
{
  unsigned char buf[sizeof(load_asm)];
  memcpy(buf, load_asm, sizeof(load_asm));
  mprotect(ALIGN(buf, PAGE_SIZE), PAGE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC);
  return call_mem(buf);
}

int asm_heap()
{
  unsigned char *buf = memalign(PAGE_SIZE, PAGE_SIZE);
  int result;
  assert(buf);
  memcpy(buf, load_asm, sizeof(load_asm));
  mprotect(buf, PAGE_SIZE, PROT_READ|PROT_EXEC);
  result = call_mem(buf);
  free(buf);
  return result;
}

int asm_mmap_rw()
{
  int result;
  unsigned char *buf = mmap(NULL, sizeof(load_asm),
                            PROT_EXEC|PROT_READ|PROT_WRITE,
                            MAP_SHARED|MAP_ANONYMOUS,
                            -1, 0);
  if (buf == MAP_FAILED)
    return 0;

  memcpy(buf, load_asm, sizeof(load_asm));
  result = call_mem(buf);
  munmap(buf, sizeof(load_asm));
  return result;
}

int asm_mmap_chg()
{
  int result;
  unsigned char *buf = mmap(NULL, sizeof(load_asm),
                            PROT_READ|PROT_WRITE,
                            MAP_SHARED|MAP_ANONYMOUS,
                            -1, 0);
  if (buf == MAP_FAILED)
    return 0;

  memcpy(buf, load_asm, sizeof(load_asm));
  mprotect(buf, sizeof(load_asm), PROT_READ|PROT_EXEC);
  result = call_mem(buf);
  munmap(buf, sizeof(load_asm));
  return result;
}

int main()
{
#define ok(X) ((X) == MAGIC ? "ok" : "FAILURE")

  printf("plain_c      = %s\n", ok(plain_c()));
  printf("plain_asm    = %s\n", ok(plain_asm()));
  printf("indirect_c   = %s\n", ok(indirect_c()));
  printf("indirect_asm = %s\n", ok(indirect_asm()));
  printf("asm_text     = %s\n", ok(asm_text()));
  printf("asm_stack    = %s\n", ok(asm_stack()));
  printf("asm_heap     = %s\n", ok(asm_heap()));
  printf("asm_mmap_rw  = %s\n", ok(asm_mmap_rw()));
  printf("asm_mmap_chg = %s\n", ok(asm_mmap_chg()));

  return 0;
}
