Today someone asked me why the following code works
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/mman.h> #include <limits.h> #include <features.h> void segv_handler(int sig, siginfo_t *sip, void *c) { struct sigcontext *scp = (struct sigcontext *)c; unsigned long i = *((unsigned long*)scp->sc_ip); printf("%s fault\n", ((i >> 21) & 1) ? "write" : "read"); exit(1); } int main(void) { struct sigaction sa; int *x; int m; sa.sa_handler = SIG_DFL; sa.sa_sigaction = segv_handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask,SIGIO); sigaddset(&sa.sa_mask,SIGALRM); sa.sa_flags = SA_SIGINFO; if (sigaction(SIGSEGV,&sa,NULL)) { printf(" Error assigning signal!\n"); } x = valloc(1024); mprotect(x,1024,PROT_NONE); //make it none access /* read fault */ m = x[0]; /* write fault */ /* x[0] = 1; */ return 0; }
This appears to be an extremley bad way to find out if you have taken a read or write fault with a SEGV on IA64.
It's actually a fluke that this works at all. Its trying to match part of the instruction opcode to see if it is a load or store but gets it completely wrong. Compile it with ICC and it will fail.
On IA64 we are provided with si_isr, the interrupt status register, which can tell us exactly what has happened. Below is the right way to do it.
#define _GNU_SOURCE 1 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/mman.h> #include <limits.h> #include <features.h> void segv_handler(int sig, siginfo_t *sip, void *c) { struct sigcontext *scp = (struct sigcontext *)c; unsigned long i = *((unsigned long*)scp->sc_ip); int x = (sip->si_isr >> 32) & 1; // bit 32 int w = (sip->si_isr >> 33) & 1; // bit 33 int r = (sip->si_isr >> 34) & 1; // bit 34 printf("%s|%s|%s fault\n", r ? "r" : " ", w ? "w" : " ", x ? "x" : " "); exit(1); } int main(void) { struct sigaction sa; volatile int *x; int m = 100; sa.sa_handler = SIG_DFL; sa.sa_sigaction = segv_handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask,SIGIO); sigaddset(&sa.sa_mask,SIGALRM); sa.sa_flags = SA_SIGINFO; if (sigaction(SIGSEGV,&sa,NULL)) { printf(" Error assigning signal!\n"); } x = valloc(1024); mprotect(x,1024,PROT_NONE); //make it none access /* read fault */ m = x[0]; /* write fault */ //x[0] = 100; return 0; }