[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#447036: gnumach: panic: fp_intr: already caught intr



On Wed, Oct 17, 2007 at 06:14:15PM +0200, Michael Banck wrote:
> static void fpe_sig_handler(int sig)
> {
>     erl_fp_exception = 1;
> }
> 
> static void do_init(void)
> {
>     signal(SIGFPE, fpe_sig_handler);
>     unmask_fpe();
> }

Hrm, maybe I forgot to mention that the original code has some more
involved signal setup conditionalized to __linux__ and __*BSD__, I'm
attaching the full code.


Michael
/* confdefs.h.  */
#define PACKAGE_NAME ""
#define PACKAGE_TARNAME ""
#define PACKAGE_VERSION ""
#define PACKAGE_STRING ""
#define PACKAGE_BUGREPORT ""
#define STDC_HEADERS 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STRING_H 1
#define HAVE_MEMORY_H 1
#define HAVE_STRINGS_H 1
#define HAVE_INTTYPES_H 1
#define HAVE_STDINT_H 1
#define HAVE_UNISTD_H 1
#define SIZEOF_VOID_P 4
#define HAVE_LIBM 1
#define HAVE_LIBDL 1
#define HAVE_LIBUTIL 1
#define ETHR_PTHREADS 1
#define ETHR_HAVE_PTHREAD_H 1
#define ETHR_HAVE_SYS_TIME_H 1
#define ETHR_TIME_WITH_SYS_TIME 1
#define ETHR_HAVE_PTHREAD_ATFORK 1
#define ETHR_HAVE_PTHREAD_MUTEXATTR_SETTYPE 1
#define ETHR_HAVE_PTHREAD_SPIN_LOCK 1
#define ETHR_HAVE_PTHREAD_ATTR_SETGUARDSIZE 1
#define ETHR_HAVE_ETHREAD_DEFINES 1
#define USE_RECURSIVE_MALLOC_MUTEX 1
#define ERTS_NEED_DLOPEN_BEFORE_DLERROR 1
#define DISABLE_CHILD_WAITER_THREAD 1
#define HAVE_GETHOSTBYNAME_R GHBN_R_GLIBC
#define NETDB_H_NEEDS_IN_H 1
#define HAVE_SOCKLEN_T 1
#define H_ERRNO_DECLARED 1
#define HAVE_DIRENT_H 1
#define STDC_HEADERS 1
#define HAVE_SYS_WAIT_H 1
#define TIME_WITH_SYS_TIME 1
#define HAVE_FCNTL_H 1
#define HAVE_LIMITS_H 1
#define HAVE_UNISTD_H 1
#define HAVE_SYSLOG_H 1
#define HAVE_DLFCN_H 1
#define HAVE_SYS_STROPTS_H 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_TIME_H 1
#define HAVE_SYS_UIO_H 1
#define HAVE_SYS_SOCKET_H 1
#define HAVE_MALLOC_H 1
#define HAVE_ARPA_NAMESER_H 1
#define HAVE_PTY_H 1
#define HAVE_UTMP_H 1
#define HAVE_DECL_SCTP_UNORDERED 0
#define HAVE_DECL_SCTP_ADDR_OVER 0
#define HAVE_DECL_SCTP_ABORT 0
#define HAVE_DECL_SCTP_EOF 0
#define HAVE_DECL_SCTP_SENDALL 0
#define RETSIGTYPE void
#define USE_MATHERR 1
#define SIZEOF_CHAR 1
#define SIZEOF_SHORT 2
#define SIZEOF_INT 4
#define SIZEOF_LONG 4
#define SIZEOF_VOID_P 4
#define SIZEOF_LONG_LONG 8
#define SIZEOF_SIZE_T 4
#define SIZEOF_OFF_T 8
#define HAVE_GETHOSTBYNAME2 1
#define HAVE_STRUCT_IN6_ADDR 1
#define HAVE_GETNAMEINFO 1
#define HAVE_INET_PTON 1
#define HAVE_STRUCT_SOCKADDR_STORAGE 1
#define HAVE_FINITE 1
#define HAVE_ISNAN 1
#define HAVE_ISINF 1
#define HAVE_DLOPEN 1
#define HAVE_PREAD 1
#define HAVE_PWRITE 1
#define HAVE_WRITEV 1
#define HAVE_MEMMOVE 1
#define HAVE_STRERROR 1
#define HAVE_STRERROR_R 1
#define HAVE_STRNCASECMP 1
#define HAVE_LOCALTIME_R 1
#define HAVE_GMTIME_R 1
#define HAVE_MEMCPY 1
#define HAVE_MALLOPT 1
#define HAVE_SBRK 1
#define HAVE___SBRK 1
#define HAVE_BRK 1
#define HAVE_FLOCKFILE 1
#define HAVE_FSTAT 1
#define PROTOTYPES 1
#define __PROTOTYPES 1
#define HAVE_FORK 1
#define HAVE_VFORK 1
#define HAVE_WORKING_VFORK 1
#define HAVE_WORKING_FORK 1
#define HAVE_VPRINTF 1
#define HAVE_STDLIB_H 1
#define HAVE_UNISTD_H 1
#define HAVE_GETPAGESIZE 1
#define HAVE_MMAP 1
#define HAVE_CONFLICTING_FREAD_DECLARATION 1
#define HAVE_PUTC_UNLOCKED 1
#define HAVE_FWRITE_UNLOCKED 1
#define HAVE_OPENPTY 1
#define HAVE_END_SYMBOL 1
#define HAVE__END_SYMBOL 1
#define ERTS___AFTER_MORECORE_HOOK_CAN_TRACK_MALLOC 1
#define SBRK_RET_TYPE void *
#define SBRK_ARG_TYPE intptr_t
#define BRK_RET_TYPE int
#define BRK_ARG_TYPE void *
#define HAVE_IN6 1
#define HAVE_MULTICAST_SUPPORT 1
/* end confdefs.h.  */

/* fpe-test.c */
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

volatile int erl_fp_exception;

/* Is there no standard identifier for Darwin/MacOSX ? */
#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
#define __DARWIN__ 1
#endif

#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) && !defined(__CYGWIN__)

static void unmask_x87(void)
{
    unsigned short cw;
    __asm__ __volatile__("fstcw %0" : "=m"(cw));
    cw &= ~(0x01|0x04|0x08);   /* unmask IM, ZM, OM */
    __asm__ __volatile__("fldcw %0" : : "m"(cw));
}

static void unmask_sse2(void)
{
    unsigned int mxcsr;
    __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
    mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */
    __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
}

#if defined(__x86_64__) || defined(__DARWIN__)
static inline int cpu_has_sse2(void) { return 1; }
#else /* !__x86_64__ */
/*
 * Check if an x86-32 processor has SSE2.
 */
static unsigned int xor_eflags(unsigned int mask)
{
    unsigned int eax, edx;

    eax = mask;			/* eax = mask */
    __asm__("pushfl\n\t"
	    "popl %0\n\t"	/* edx = original EFLAGS */
	    "xorl %0, %1\n\t"	/* eax = mask ^ EFLAGS */
	    "pushl %1\n\t"
	    "popfl\n\t"		/* new EFLAGS = mask ^ original EFLAGS */
	    "pushfl\n\t"
	    "popl %1\n\t"	/* eax = new EFLAGS */
	    "xorl %0, %1\n\t"	/* eax = new EFLAGS ^ old EFLAGS */
	    "pushl %0\n\t"
	    "popfl"		/* restore original EFLAGS */
	    : "=d"(edx), "=a"(eax)
	    : "1"(eax));
    return eax;
}

static __inline__ unsigned int cpuid_eax(unsigned int op)
{
    unsigned int eax;
    __asm__("cpuid"
	    : "=a"(eax)
	    : "0"(op)
	    : "bx", "cx", "dx");
    return eax;
}

static __inline__ unsigned int cpuid_edx(unsigned int op)
{
    unsigned int eax, edx;
    __asm__("cpuid"
	    : "=a"(eax), "=d"(edx)
	    : "0"(op)
	    : "bx", "cx");
    return edx;
}

/* The AC bit, bit #18, is a new bit introduced in the EFLAGS
 * register on the Intel486 processor to generate alignment
 * faults. This bit cannot be set on the Intel386 processor.
 */
static __inline__ int is_386(void)
{
    return ((xor_eflags(1<<18) >> 18) & 1) == 0;
}

/* Newer x86 processors have a CPUID instruction, as indicated by
 * the ID bit (#21) in EFLAGS being modifiable.
 */
static __inline__ int has_CPUID(void)
{
    return (xor_eflags(1<<21) >> 21) & 1;
}

static int cpu_has_sse2(void)
{
    unsigned int maxlev, features;
    static int has_sse2 = -1;

    if (has_sse2 >= 0)
	return has_sse2;
    has_sse2 = 0;

    if (is_386())
	return 0;
    if (!has_CPUID())
	return 0;
    maxlev = cpuid_eax(0);
    /* Intel A-step Pentium had a preliminary version of CPUID.
       It also didn't have SSE2. */
    if ((maxlev & 0xFFFFFF00) == 0x0500)
	return 0;
    /* If max level is zero then CPUID cannot report any features. */
    if (maxlev == 0)
	return 0;
    features = cpuid_edx(1);
    has_sse2 = (features & (1 << 26)) != 0;

    return has_sse2;
}
#endif /* !__x86_64__ */

static void unmask_fpe(void)
{
    unmask_x87();
    if (cpu_has_sse2())
	unmask_sse2();
}

static __inline__ int check_fpe(double f)
{
    __asm__ __volatile__("fwait" : "=m"(erl_fp_exception) : "m"(f));
    if (!erl_fp_exception)
       return 0;
    __asm__ __volatile__("fninit");
    unmask_fpe();
    return 1;
}

#elif defined(__sparc__) && defined(__linux__)

static void unmask_fpe(void)
{
    unsigned long fsr;

    __asm__("st %%fsr, %0" : "=m"(fsr));
    fsr &= ~(0x1FUL << 23);	/* clear FSR[TEM] field */
    fsr |= (0x1AUL << 23);	/* enable NV, OF, DZ exceptions */
    __asm__ __volatile__("ld %0, %%fsr" : : "m"(fsr));
}

static __inline__ int check_fpe(double f)
{
    __asm__ __volatile__("" : "=m"(erl_fp_exception) : "em"(f));
    return erl_fp_exception;
}

#elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__))

#if defined(__linux__)
#include <sys/prctl.h>

static void set_fpexc_precise(void)
{
    if (prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE) < 0) {
	perror("PR_SET_FPEXC");
	exit(1);
    }
}

#elif defined(__DARWIN__)

#include <mach/mach.h>
#include <pthread.h>

/*
 * FE0 FE1	MSR bits
 *  0   0	floating-point exceptions disabled
 *  0   1	floating-point imprecise nonrecoverable
 *  1   0	floating-point imprecise recoverable
 *  1   1	floating-point precise mode
 *
 * Apparently:
 * - Darwin 5.5 (MacOS X <= 10.1) starts with FE0 == FE1 == 0,
 *   and resets FE0 and FE1 to 0 after each SIGFPE.
 * - Darwin 6.0 (MacOS X 10.2) starts with FE0 == FE1 == 1,
 *   and does not reset FE0 or FE1 after a SIGFPE.
 */
#define FE0_MASK	(1<<11)
#define FE1_MASK	(1<<8)

/* a thread cannot get or set its own MSR bits */
static void *fpu_fpe_enable(void *arg)
{
    thread_t t = *(thread_t*)arg;
    struct ppc_thread_state state;
    unsigned int state_size = PPC_THREAD_STATE_COUNT;

    if (thread_get_state(t, PPC_THREAD_STATE, (natural_t*)&state, &state_size) != KERN_SUCCESS) {
	perror("thread_get_state");
	exit(1);
    }
    if ((state.srr1 & (FE1_MASK|FE0_MASK)) != (FE1_MASK|FE0_MASK)) {
#if 0
	/* This would also have to be performed in the SIGFPE handler
	   to work around the MSR reset older Darwin releases do. */
	state.srr1 |= (FE1_MASK|FE0_MASK);
	thread_set_state(t, PPC_THREAD_STATE, (natural_t*)&state, state_size);
#else
	fprintf(stderr, "srr1 == 0x%08x, your Darwin is too old\n", state.srr1);
	exit(1);
#endif
    }
    return NULL; /* Ok, we appear to be on Darwin 6.0 or later */
}

static void set_fpexc_precise(void)
{
    thread_t self = mach_thread_self();
    pthread_t enabler;

    if (pthread_create(&enabler, NULL, fpu_fpe_enable, &self)) {
	perror("pthread_create");
    } else if (pthread_join(enabler, NULL)) {
	perror("pthread_join");
    }
}

#endif

static void set_fpscr(unsigned int fpscr)
{
    union {
	double d;
	unsigned int fpscr[2];
    } u;
    u.fpscr[0] = 0xFFF80000;
    u.fpscr[1] = fpscr;
    __asm__ __volatile__("mtfsf 255,%0" : : "f"(u.d));
}

static void unmask_fpe(void)
{
    set_fpexc_precise();
    set_fpscr(0x80|0x40|0x10);	/* VE, OE, ZE; not UE or XE */
}

static __inline__ int check_fpe(double f)
{
    __asm__ __volatile__("" : "=m"(erl_fp_exception) : "fm"(f));
    return erl_fp_exception;
}

#else

#include <ieeefp.h>
#define unmask_fpe()   fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ)
static __inline__ int check_fpe(double f)
{
    __asm__ __volatile__("" : "=m"(erl_fp_exception) : "g"(f));
    return erl_fp_exception;
}

#endif

#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__i386__) || defined(__x86_64__))) || (defined(__OpenBSD__) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))

#if defined(__linux__) && defined(__i386__)
#include <asm/sigcontext.h>
#elif defined(__FreeBSD__) && defined(__i386__)
#include <sys/types.h>
#include <machine/npx.h>
#elif defined(__FreeBSD__) && defined(__x86_64__)
#include <sys/types.h>
#include <machine/fpu.h>
#elif defined(__OpenBSD__) && defined(__x86_64__)
#include <sys/types.h>
#include <machine/fpu.h>
#endif
#if !(defined(__OpenBSD__) && defined(__x86_64__))
#include <ucontext.h>
#endif
#include <string.h>

static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
{
    ucontext_t *uc = puc;
#if defined(__linux__)
#if defined(__x86_64__)
    mcontext_t *mc = &uc->uc_mcontext;
    fpregset_t fpstate = mc->fpregs;
    fpstate->mxcsr = 0x1F80;
    fpstate->swd &= ~0xFF;
#elif defined(__i386__)
    mcontext_t *mc = &uc->uc_mcontext;
    fpregset_t fpstate = mc->fpregs;
    if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
	((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
    fpstate->sw &= ~0xFF;
#elif defined(__sparc__)
    /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
    struct sigcontext *sc = (struct sigcontext*)puc;
    sc->si_regs.pc = sc->si_regs.npc;
    sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4;
#elif defined(__powerpc__)
#if defined(__powerpc64__)
    mcontext_t *mc = &uc->uc_mcontext;
    unsigned long *regs = &mc->gp_regs[0];
#else
    mcontext_t *mc = uc->uc_mcontext.uc_regs;
    unsigned long *regs = &mc->gregs[0];
#endif
    regs[PT_NIP] += 4;
    regs[PT_FPSCR] = 0x80|0x40|0x10;	/* VE, OE, ZE; not UE or XE */
#endif
#elif defined(__DARWIN__) && defined(__i386__)
    mcontext_t mc = uc->uc_mcontext;
    i386_float_state_t *fpstate = &mc->fs;
    fpstate->fpu_mxcsr = 0x1F80;
    *(unsigned short *)&fpstate->fpu_fsw &= ~0xFF;
#elif defined(__DARWIN__) && defined(__ppc__)
    mcontext_t mc = uc->uc_mcontext;
    mc->ss.srr0 += 4;
    mc->fs.fpscr = 0x80|0x40|0x10;
#elif defined(__FreeBSD__) && defined(__x86_64__)
    mcontext_t *mc = &uc->uc_mcontext;
    struct savefpu *savefpu = (struct savefpu*)&mc->mc_fpstate;
    struct envxmm *envxmm = &savefpu->sv_env;
    envxmm->en_mxcsr = 0x1F80;
    envxmm->en_sw &= ~0xFF;
#elif defined(__FreeBSD__) && defined(__i386__)
    mcontext_t *mc = &uc->uc_mcontext;
    union savefpu *savefpu = (union savefpu*)&mc->mc_fpstate;
    if (mc->mc_fpformat == _MC_FPFMT_XMM) {
	struct envxmm *envxmm = &savefpu->sv_xmm.sv_env;
	envxmm->en_mxcsr = 0x1F80;
	envxmm->en_sw &= ~0xFF;
    } else {
	struct env87 *env87 = &savefpu->sv_87.sv_env;
	env87->en_sw &= ~0xFF;
    }
#elif defined(__OpenBSD__) && defined(__x86_64__)
    struct fxsave64 *fxsave = uc->sc_fpstate;
    fxsave->fx_mxcsr = 0x1F80;
    fxsave->fx_fsw &= ~0xFF;
#elif defined(__sun__) && defined(__x86_64__)
    mcontext_t *mc = &uc->uc_mcontext;
    struct fpchip_state *fpstate = &mc->fpregs.fp_reg_set.fpchip_state;
    fpstate->mxcsr = 0x1F80;
    fpstate->sw &= ~0xFF;
#endif
    erl_fp_exception = 1;
}

static void do_init(void)
{
    struct sigaction act;
    memset(&act, 0, sizeof act);
    act.sa_sigaction = fpe_sig_action;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGFPE, &act, NULL);
    unmask_fpe();
}

#else

static void fpe_sig_handler(int sig)
{
    erl_fp_exception = 1;
}

static void do_init(void)
{
    signal(SIGFPE, fpe_sig_handler);
    unmask_fpe();
}

#endif

double a = 3.23e133;
double b = 3.57e257;
double res;

void do_fmul(void)
{
    res = a * b;
}

int do_check(void)
{
    if (check_fpe(res)) {
       fprintf(stderr, "res = %g, FPE worked\n", res);
       return 0;
    } else {
       fprintf(stderr, "res = %g, FPE failed\n", res);
       return 1;
    }
}

int main(int argc, const char **argv)
{
    if (argc == 3) {
       a = atof(argv[1]);
       b = atof(argv[2]);
    }
    do_init();
    do_fmul();
    return do_check();
}


Reply to: