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