mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-08 08:26:14 +00:00
192 lines
4.2 KiB
C
192 lines
4.2 KiB
C
/*
|
|
Check that a fault signal handler gets the expected info
|
|
*/
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <setjmp.h>
|
|
#include "tests/sys_mman.h"
|
|
#include <unistd.h>
|
|
|
|
/* Division by zero triggers a SIGFPE on x86 and x86_64,
|
|
but not on the PowerPC architecture.
|
|
|
|
On ARM-Linux, we do get a SIGFPE, but not from the faulting of a
|
|
division instruction (there isn't any such thing) but rather
|
|
because the process exits via tgkill, sending itself a SIGFPE.
|
|
Hence we get a SIGFPE but the SI_CODE is different from that on
|
|
x86/amd64-linux.
|
|
*/
|
|
#if defined(__powerpc__) || defined(__aarch64__)
|
|
# define DIVISION_BY_ZERO_TRIGGERS_FPE 0
|
|
# define DIVISION_BY_ZERO_SI_CODE SI_TKILL
|
|
#elif defined(__arm__)
|
|
# define DIVISION_BY_ZERO_TRIGGERS_FPE 1
|
|
# define DIVISION_BY_ZERO_SI_CODE SI_TKILL
|
|
#else
|
|
# define DIVISION_BY_ZERO_TRIGGERS_FPE 1
|
|
# define DIVISION_BY_ZERO_SI_CODE FPE_INTDIV
|
|
#endif
|
|
|
|
/* Accessing non-mapped virtual address results in SIGBUS
|
|
* with si_code equal to BUS_ADRERR on Linux, whereas in SIGBUS
|
|
* with si_code equal to BUS_OBJERR on Solaris. On Solaris,
|
|
* BUS_ADRERR is used for bus time out while BUS_OBJERR is translated
|
|
* from underlying codes FC_OBJERR (x86) or ASYNC_BERR (sparc).
|
|
*/
|
|
#if defined(VGO_solaris)
|
|
# define BUS_ERROR_SI_CODE BUS_OBJERR
|
|
#else
|
|
# define BUS_ERROR_SI_CODE BUS_ADRERR
|
|
#endif
|
|
|
|
struct test {
|
|
void (*test)(void);
|
|
int sig;
|
|
int code;
|
|
volatile void *addr;
|
|
};
|
|
|
|
static const struct test *cur_test;
|
|
|
|
static int zero();
|
|
|
|
static sigjmp_buf escape;
|
|
|
|
#define BADADDR ((int *)0x1234)
|
|
|
|
#define FILESIZE (4*__pagesize)
|
|
#define MAPSIZE (2*FILESIZE)
|
|
static unsigned int __pagesize;
|
|
static char volatile *volatile mapping;
|
|
|
|
static int testsig(int sig, int want)
|
|
{
|
|
if (sig != want) {
|
|
fprintf(stderr, " FAIL: expected signal %d, not %d\n", want, sig);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int testcode(int code, int want)
|
|
{
|
|
if (code != want) {
|
|
fprintf(stderr, " FAIL: expected si_code==%d, not %d\n", want, code);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int testaddr(void *addr, volatile void *want)
|
|
{
|
|
/* Some architectures (e.g. s390) just provide enough information to
|
|
resolve the page fault, but do not provide the offset within a page */
|
|
#if defined(__s390__)
|
|
if (addr != (void *) ((unsigned long) want & ~0xffful)) {
|
|
#else
|
|
if (addr != want) {
|
|
#endif
|
|
fprintf(stderr, " FAIL: expected si_addr==%p, not %p\n", want, addr);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
|
|
}
|
|
|
|
static void handler(int sig, siginfo_t *si, void *uc)
|
|
{
|
|
int ok = 1;
|
|
|
|
ok = ok && testsig(sig, cur_test->sig);
|
|
ok = ok && testcode(si->si_code, cur_test->code);
|
|
if (cur_test->addr)
|
|
ok = ok && testaddr(si->si_addr, cur_test->addr);
|
|
|
|
if (ok)
|
|
fprintf(stderr, " PASS\n");
|
|
|
|
siglongjmp(escape, ok + 1);
|
|
}
|
|
|
|
|
|
static void test1(void)
|
|
{
|
|
*BADADDR = 'x';
|
|
}
|
|
|
|
static void test2()
|
|
{
|
|
mapping[0] = 'x';
|
|
}
|
|
|
|
static void test3()
|
|
{
|
|
mapping[FILESIZE+10];
|
|
}
|
|
|
|
static void test4()
|
|
{
|
|
volatile int v = 44/zero();
|
|
|
|
(void)v;
|
|
#if DIVISION_BY_ZERO_TRIGGERS_FPE == 0
|
|
raise(SIGFPE);
|
|
#endif
|
|
}
|
|
|
|
int main()
|
|
{
|
|
int fd, i;
|
|
static const int sigs[] = { SIGSEGV, SIGILL, SIGBUS, SIGFPE, SIGTRAP };
|
|
struct sigaction sa;
|
|
__pagesize = (unsigned int)sysconf(_SC_PAGE_SIZE);
|
|
sa.sa_sigaction = handler;
|
|
sa.sa_flags = SA_SIGINFO;
|
|
sigfillset(&sa.sa_mask);
|
|
|
|
for(i = 0; i < sizeof(sigs)/sizeof(*sigs); i++)
|
|
sigaction(sigs[i], &sa, NULL);
|
|
|
|
/* we need O_RDWR for the truncate below */
|
|
fd = open("faultstatus.tmp", O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600);
|
|
if (fd == -1) {
|
|
perror("tmpfile");
|
|
exit(1);
|
|
}
|
|
unlink("faultstatus.tmp");
|
|
ftruncate(fd, FILESIZE);
|
|
|
|
mapping = mmap(0, MAPSIZE, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
close(fd);
|
|
|
|
{
|
|
const struct test tests[] = {
|
|
#define T(n, sig, code, addr) { test##n, sig, code, addr }
|
|
T(1, SIGSEGV, SEGV_MAPERR, BADADDR),
|
|
T(2, SIGSEGV, SEGV_ACCERR, mapping),
|
|
T(3, SIGBUS, BUS_ERROR_SI_CODE, &mapping[FILESIZE+10]),
|
|
T(4, SIGFPE, DIVISION_BY_ZERO_SI_CODE, 0),
|
|
#undef T
|
|
};
|
|
|
|
for(i = 0; i < sizeof(tests)/sizeof(*tests); i++) {
|
|
cur_test = &tests[i];
|
|
|
|
if (sigsetjmp(escape, 1) == 0) {
|
|
fprintf(stderr, "Test %d: ", i+1);
|
|
tests[i].test();
|
|
fprintf(stderr, " FAIL: no fault, or handler returned\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zero()
|
|
{
|
|
return 0;
|
|
}
|