
#undef _GNU_SOURCE
#define _GNU_SOURCE 1

#include <signal.h>
#include <stdio.h>
#include <sys/ucontext.h>

static char* rip_at_sig = NULL;


static void int_handler(int signum, siginfo_t *si, void *uc_arg)
{
   ucontext_t *uc = (ucontext_t *)uc_arg;
   /* Note that uc->uc_mcontext is an embedded struct, not a pointer */
   mcontext_t *mc = &(uc->uc_mcontext);
#if defined(__FreeBSD__)
   void *pc = (void*)mc->mc_rip;
#else
   void *pc = (void*)mc->gregs[REG_RIP];
#endif
   printf("in int_handler, RIP is ...\n");
   rip_at_sig = pc;
}

static void register_handler(int sig, void *handler)
{
   struct sigaction sa;
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   sigfillset(&sa.sa_mask);
   sa.sa_sigaction = handler;
   sigaction(sig, &sa, NULL);
}

int main(void) {
   char *intaddr = NULL;
   puts("main");
   register_handler(SIGTRAP, int_handler);
   asm volatile(
      "movabsq $zz_int, %%rdx\n"
      "mov %%rdx, %0\n"
      "zz_int:\n"
      "int $3\n"
      : /* no outputs */
      : "m" (intaddr) /* input: address of var to store target addr to */
      : /* clobbers */ "rdx"
      );
   /* intaddr is the address of the int 3 insn.  rip_at_sig is the PC
      after the exception, which should be the next insn along.
      Hence: */
   if (intaddr != NULL && rip_at_sig != NULL
       && rip_at_sig == intaddr+1)
     printf("PASS\n");
   else
     printf("FAIL\n");
   return 0;
}
