1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2001-2018. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 
25 #include "sys.h"
26 #include "global.h"
27 #include "erl_process.h"
28 
29 
30 #ifdef NO_FPE_SIGNALS
31 
32 void
erts_sys_init_float(void)33 erts_sys_init_float(void)
34 {
35 # ifdef SIGFPE
36     sys_signal(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */
37 # endif
38 }
39 
40 #else  /* !NO_FPE_SIGNALS */
41 
42 static erts_tsd_key_t fpe_key;
43 
44 /* once-only initialisation early in the main thread (via erts_sys_init_float()) */
erts_init_fp_exception(void)45 static void erts_init_fp_exception(void)
46 {
47     /* XXX: the wrappers prevent using a pthread destructor to
48        deallocate the key's value; so when/where do we do that? */
49     erts_tsd_key_create(&fpe_key,"fp_exception");
50 }
51 
erts_thread_init_fp_exception(void)52 void erts_thread_init_fp_exception(void)
53 {
54     unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe));
55     *fpe = 0;
56     erts_tsd_set(fpe_key, fpe);
57 }
58 
erts_thread_get_fp_exception(void)59 static ERTS_INLINE volatile unsigned long *erts_thread_get_fp_exception(void)
60 {
61     return (volatile unsigned long*)erts_tsd_get(fpe_key);
62 }
63 
erts_get_current_fp_exception(void)64 volatile unsigned long *erts_get_current_fp_exception(void)
65 {
66     Process *c_p;
67 
68     c_p = erts_get_current_process();
69     if (c_p)
70 	return &c_p->fp_exception;
71     return erts_thread_get_fp_exception();
72 }
73 
set_current_fp_exception(unsigned long pc)74 static void set_current_fp_exception(unsigned long pc)
75 {
76     volatile unsigned long *fpexnp = erts_get_current_fp_exception();
77     ASSERT(fpexnp != NULL);
78     *fpexnp = pc;
79 }
80 
erts_fp_check_init_error(volatile unsigned long * fpexnp)81 void erts_fp_check_init_error(volatile unsigned long *fpexnp)
82 {
83     char buf[128];
84     snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n",
85 	     __builtin_return_address(0), (void*)*fpexnp);
86     if (write(2, buf, strlen(buf)) <= 0)
87 	erts_exit(ERTS_ABORT_EXIT, "%s", buf);
88     *fpexnp = 0;
89 #if defined(__i386__) || defined(__x86_64__)
90     erts_restore_fpu();
91 #endif
92 }
93 
94 /* Is there no standard identifier for Darwin/MacOSX ? */
95 #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
96 #define __DARWIN__ 1
97 #endif
98 
99 /*
100  * Define two processor and possibly OS-specific primitives:
101  *
102  * static void unmask_fpe(void);
103  * -- unmask invalid, overflow, and divide-by-zero exceptions
104  *
105  * static int mask_fpe(void);
106  * -- mask invalid, overflow, and divide-by-zero exceptions
107  * -- return non-zero if the previous state was unmasked
108  */
109 
110 #if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
111 
unmask_x87(void)112 static void unmask_x87(void)
113 {
114     unsigned short cw;
115 
116     __asm__ __volatile__("fstcw %0" : "=m"(cw));
117     cw &= ~(0x01|0x04|0x08);   /* unmask IM, ZM, OM */
118     __asm__ __volatile__("fldcw %0" : : "m"(cw));
119 }
120 
mask_x87(void)121 static int mask_x87(void)
122 {
123     unsigned short cw;
124     int unmasked;
125 
126     __asm__ __volatile__("fstcw %0" : "=m"(cw));
127     unmasked = (cw & (0x01|0x04|0x08)) == 0;
128     /* or just set cw = 0x37f */
129     cw |= (0x01|0x04|0x08); /* mask IM, ZM, OM */
130     __asm__ __volatile__("fldcw %0" : : "m"(cw));
131     return unmasked;
132 }
133 
unmask_sse2(void)134 static void unmask_sse2(void)
135 {
136     unsigned int mxcsr;
137 
138     __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
139     mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */
140     __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
141 }
142 
mask_sse2(void)143 static int mask_sse2(void)
144 {
145     unsigned int mxcsr;
146     int unmasked;
147 
148     __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
149     unmasked = (mxcsr & 0x0680) == 0;
150     /* or just set mxcsr = 0x1f80 */
151     mxcsr &= ~0x003F; /* clear exn flags */
152     mxcsr |=  0x0680; /* mask OM, ZM, IM (not PM, UM, DM) */
153     __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
154     return unmasked;
155 }
156 
157 #if defined(__x86_64__)
158 
cpu_has_sse2(void)159 static ERTS_INLINE int cpu_has_sse2(void) { return 1; }
160 
161 #else /* !__x86_64__ */
162 
163 /*
164  * Check if an x86-32 processor has SSE2.
165  */
xor_eflags(unsigned int mask)166 static unsigned int xor_eflags(unsigned int mask)
167 {
168     unsigned int eax, edx;
169 
170     eax = mask;			/* eax = mask */
171     __asm__("pushfl\n\t"
172 	    "popl %0\n\t"	/* edx = original EFLAGS */
173 	    "xorl %0, %1\n\t"	/* eax = mask ^ EFLAGS */
174 	    "pushl %1\n\t"
175 	    "popfl\n\t"		/* new EFLAGS = mask ^ original EFLAGS */
176 	    "pushfl\n\t"
177 	    "popl %1\n\t"	/* eax = new EFLAGS */
178 	    "xorl %0, %1\n\t"	/* eax = new EFLAGS ^ old EFLAGS */
179 	    "pushl %0\n\t"
180 	    "popfl"		/* restore original EFLAGS */
181 	    : "=d"(edx), "=a"(eax)
182 	    : "1"(eax));
183     return eax;
184 }
185 
cpuid_eax(unsigned int op)186 static ERTS_INLINE unsigned int cpuid_eax(unsigned int op)
187 {
188     unsigned int eax, save_ebx;
189 
190     /* In PIC mode i386 reserves EBX. So we must save
191        and restore it ourselves to not upset gcc. */
192     __asm__(
193 	"movl %%ebx, %1\n\t"
194 	"cpuid\n\t"
195 	"movl %1, %%ebx"
196 	: "=a"(eax), "=m"(save_ebx)
197 	: "0"(op)
198 	: "cx", "dx");
199     return eax;
200 }
201 
cpuid_edx(unsigned int op)202 static ERTS_INLINE unsigned int cpuid_edx(unsigned int op)
203 {
204     unsigned int eax, edx, save_ebx;
205 
206     /* In PIC mode i386 reserves EBX. So we must save
207        and restore it ourselves to not upset gcc. */
208     __asm__(
209 	"movl %%ebx, %2\n\t"
210 	"cpuid\n\t"
211 	"movl %2, %%ebx"
212 	: "=a"(eax), "=d"(edx), "=m"(save_ebx)
213 	: "0"(op)
214 	: "cx");
215     return edx;
216 }
217 
218 /* The AC bit, bit #18, is a new bit introduced in the EFLAGS
219  * register on the Intel486 processor to generate alignment
220  * faults. This bit cannot be set on the Intel386 processor.
221  */
is_386(void)222 static ERTS_INLINE int is_386(void)
223 {
224     return ((xor_eflags(1<<18) >> 18) & 1) == 0;
225 }
226 
227 /* Newer x86 processors have a CPUID instruction, as indicated by
228  * the ID bit (#21) in EFLAGS being modifiable.
229  */
has_CPUID(void)230 static ERTS_INLINE int has_CPUID(void)
231 {
232     return (xor_eflags(1<<21) >> 21) & 1;
233 }
234 
cpu_has_sse2(void)235 static int cpu_has_sse2(void)
236 {
237     unsigned int maxlev, features;
238     static int has_sse2 = -1;
239 
240     if (has_sse2 >= 0)
241 	return has_sse2;
242     has_sse2 = 0;
243 
244     if (is_386())
245 	return 0;
246     if (!has_CPUID())
247 	return 0;
248     maxlev = cpuid_eax(0);
249     /* Intel A-step Pentium had a preliminary version of CPUID.
250        It also didn't have SSE2. */
251     if ((maxlev & 0xFFFFFF00) == 0x0500)
252 	return 0;
253     /* If max level is zero then CPUID cannot report any features. */
254     if (maxlev == 0)
255 	return 0;
256     features = cpuid_edx(1);
257     has_sse2 = (features & (1 << 26)) != 0;
258 
259     return has_sse2;
260 }
261 #endif /* !__x86_64__ */
262 
unmask_fpe_internal(void)263 static void unmask_fpe_internal(void)
264 {
265     unmask_x87();
266     if (cpu_has_sse2())
267 	unmask_sse2();
268 }
269 
unmask_fpe(void)270 static void unmask_fpe(void)
271 {
272     __asm__ __volatile__("fnclex");
273     unmask_fpe_internal();
274 }
275 
mask_fpe(void)276 static int mask_fpe(void)
277 {
278     int unmasked;
279 
280     unmasked = mask_x87();
281     if (cpu_has_sse2())
282 	unmasked |= mask_sse2();
283     return unmasked;
284 }
285 
erts_restore_fpu(void)286 void erts_restore_fpu(void)
287 {
288     __asm__ __volatile__("fninit");
289     unmask_fpe_internal();
290 }
291 
292 #elif defined(__sparc__) && defined(__linux__)
293 
294 #if defined(__arch64__)
295 #define LDX "ldx"
296 #define STX "stx"
297 #else
298 #define LDX "ld"
299 #define STX "st"
300 #endif
301 
unmask_fpe(void)302 static void unmask_fpe(void)
303 {
304     unsigned long fsr;
305 
306     __asm__(STX " %%fsr, %0" : "=m"(fsr));
307     fsr &= ~(0x1FUL << 23);	/* clear FSR[TEM] field */
308     fsr |= (0x1AUL << 23);	/* enable NV, OF, DZ exceptions */
309     __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
310 }
311 
mask_fpe(void)312 static int mask_fpe(void)
313 {
314     unsigned long fsr;
315     int unmasked;
316 
317     __asm__(STX " %%fsr, %0" : "=m"(fsr));
318     unmasked = ((fsr >> 23) & 0x1A) == 0x1A;
319     fsr &= ~(0x1FUL << 23);	/* clear FSR[TEM] field */
320     __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
321     return unmasked;
322 }
323 
324 #elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__))
325 
326 #if defined(__linux__)
327 #include <sys/prctl.h>
328 
set_fpexc_precise(void)329 static void set_fpexc_precise(void)
330 {
331     if (prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE) < 0) {
332 	perror("PR_SET_FPEXC");
333 	exit(1);
334     }
335 }
336 
337 #elif defined(__DARWIN__)
338 
339 #include <mach/mach.h>
340 #include <pthread.h>
341 
342 /*
343  * FE0 FE1	MSR bits
344  *  0   0	floating-point exceptions disabled
345  *  0   1	floating-point imprecise nonrecoverable
346  *  1   0	floating-point imprecise recoverable
347  *  1   1	floating-point precise mode
348  *
349  * Apparently:
350  * - Darwin 5.5 (MacOS X <= 10.1) starts with FE0 == FE1 == 0,
351  *   and resets FE0 and FE1 to 0 after each SIGFPE.
352  * - Darwin 6.0 (MacOS X 10.2) starts with FE0 == FE1 == 1,
353  *   and does not reset FE0 or FE1 after a SIGFPE.
354  */
355 #define FE0_MASK	(1<<11)
356 #define FE1_MASK	(1<<8)
357 
358 /* a thread cannot get or set its own MSR bits */
fpu_fpe_enable(void * arg)359 static void *fpu_fpe_enable(void *arg)
360 {
361     thread_t t = *(thread_t*)arg;
362     struct ppc_thread_state state;
363     unsigned int state_size = PPC_THREAD_STATE_COUNT;
364 
365     if (thread_get_state(t, PPC_THREAD_STATE, (natural_t*)&state, &state_size) != KERN_SUCCESS) {
366 	perror("thread_get_state");
367 	exit(1);
368     }
369     if ((state.srr1 & (FE1_MASK|FE0_MASK)) != (FE1_MASK|FE0_MASK)) {
370 #if 1
371 	/* This would also have to be performed in the SIGFPE handler
372 	   to work around the MSR reset older Darwin releases do. */
373 	state.srr1 |= (FE1_MASK|FE0_MASK);
374 	thread_set_state(t, PPC_THREAD_STATE, (natural_t*)&state, state_size);
375 #else
376 	fprintf(stderr, "srr1 == 0x%08x, your Darwin is too old\n", state.srr1);
377 	exit(1);
378 #endif
379     }
380     return NULL; /* Ok, we appear to be on Darwin 6.0 or later */
381 }
382 
set_fpexc_precise(void)383 static void set_fpexc_precise(void)
384 {
385     thread_t self = mach_thread_self();
386     pthread_t enabler;
387 
388     if (pthread_create(&enabler, NULL, fpu_fpe_enable, &self)) {
389 	perror("pthread_create");
390     } else if (pthread_join(enabler, NULL)) {
391 	perror("pthread_join");
392     }
393 }
394 
395 #endif
396 
set_fpscr(unsigned int fpscr)397 static void set_fpscr(unsigned int fpscr)
398 {
399     union {
400 	double d;
401 	unsigned int fpscr[2];
402     } u;
403 
404     u.fpscr[0] = 0xFFF80000;
405     u.fpscr[1] = fpscr;
406     __asm__ __volatile__("mtfsf 255,%0" : : "f"(u.d));
407 }
408 
get_fpscr(void)409 static unsigned int get_fpscr(void)
410 {
411     union {
412 	double d;
413 	unsigned int fpscr[2];
414     } u;
415 
416     __asm__("mffs %0" : "=f"(u.d));
417     return u.fpscr[1];
418 }
419 
unmask_fpe(void)420 static void unmask_fpe(void)
421 {
422     set_fpexc_precise();
423     set_fpscr(0x80|0x40|0x10);	/* VE, OE, ZE; not UE or XE */
424 }
425 
mask_fpe(void)426 static int mask_fpe(void)
427 {
428     int unmasked;
429 
430     unmasked = (get_fpscr() & (0x80|0x40|0x10)) == (0x80|0x40|0x10);
431     set_fpscr(0x00);
432     return unmasked;
433 }
434 
435 #else /* !(x86 || (sparc && linux) || (powerpc && (linux || darwin))) */
436 
unmask_fpe(void)437 static void unmask_fpe(void)
438 {
439     fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ);
440 }
441 
mask_fpe(void)442 static int mask_fpe(void)
443 {
444     const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ;
445     fp_except old_mask;
446 
447     old_mask = fpsetmask(0);
448     return (old_mask & unmasked_mask) == unmasked_mask;
449 }
450 
451 #endif
452 
453 /*
454  * Define a processor and OS-specific SIGFPE handler.
455  *
456  * The effect of receiving a SIGFPE should be:
457  * 1. Update the processor context:
458  *    a) on x86: mask FP exceptions, do not skip faulting instruction
459  *    b) on SPARC and PowerPC: unmask FP exceptions, skip faulting instruction
460  * 2. call set_current_fp_exception with the PC of the faulting instruction
461  */
462 
463 #if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))
464 
465 #if defined(__linux__) && defined(__i386__)
466 #if !defined(X86_FXSR_MAGIC)
467 #define X86_FXSR_MAGIC 0x0000
468 #endif
469 #elif defined(__FreeBSD__) && defined(__x86_64__)
470 #include <sys/types.h>
471 #include <machine/fpu.h>
472 #elif defined(__FreeBSD__) && defined(__i386__)
473 #include <sys/types.h>
474 #include <machine/npx.h>
475 #elif defined(__DARWIN__)
476 #include <machine/signal.h>
477 #elif defined(__OpenBSD__) && defined(__x86_64__)
478 #include <sys/types.h>
479 #include <machine/fpu.h>
480 #endif
481 #if !(defined(__OpenBSD__) && defined(__x86_64__))
482 #include <ucontext.h>
483 #endif
484 #include <string.h>
485 
486 #if defined(__linux__) && defined(__x86_64__)
487 #define mc_pc(mc)	((mc)->gregs[REG_RIP])
488 #elif defined(__linux__) && defined(__i386__)
489 #define mc_pc(mc)	((mc)->gregs[REG_EIP])
490 #elif defined(__DARWIN__)
491 # error "Floating-point exceptions not supported on MacOS X"
492 #elif defined(__FreeBSD__) && defined(__x86_64__)
493 #define mc_pc(mc)	((mc)->mc_rip)
494 #elif defined(__FreeBSD__) && defined(__i386__)
495 #define mc_pc(mc)	((mc)->mc_eip)
496 #elif defined(__NetBSD__) && defined(__x86_64__)
497 #define mc_pc(mc)	((mc)->__gregs[_REG_RIP])
498 #elif defined(__NetBSD__) && defined(__i386__)
499 #define mc_pc(mc)	((mc)->__gregs[_REG_EIP])
500 #elif defined(__OpenBSD__) && defined(__x86_64__)
501 #define mc_pc(mc)	((mc)->sc_rip)
502 #elif defined(__sun__) && defined(__x86_64__)
503 #define mc_pc(mc)	((mc)->gregs[REG_RIP])
504 #endif
505 
fpe_sig_action(int sig,siginfo_t * si,void * puc)506 static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
507 {
508     ucontext_t *uc = puc;
509     unsigned long pc;
510 
511 #if defined(__linux__) && defined(__x86_64__)
512     mcontext_t *mc = &uc->uc_mcontext;
513     fpregset_t fpstate = mc->fpregs;
514     pc = mc_pc(mc);
515     /* A failed SSE2 instruction will restart. To avoid
516        looping we mask SSE2 exceptions now and unmask them
517        again later in erts_check_fpe()/erts_restore_fpu().
518        On RISCs we update PC to skip the failed instruction,
519        but the ever increasing complexity of the x86 instruction
520        set encoding makes that a poor solution here. */
521     fpstate->mxcsr = 0x1F80;
522     fpstate->swd &= ~0xFF;
523 #elif defined(__linux__) && defined(__i386__)
524     mcontext_t *mc = &uc->uc_mcontext;
525     fpregset_t fpstate = mc->fpregs;
526     pc = mc_pc(mc);
527     if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
528 	((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
529     fpstate->sw &= ~0xFF;
530 #elif defined(__linux__) && defined(__sparc__) && defined(__arch64__)
531     /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
532     struct sigcontext *sc = (struct sigcontext*)puc;
533     pc = sc->sigc_regs.tpc;
534     sc->sigc_regs.tpc = sc->sigc_regs.tnpc;
535     sc->sigc_regs.tnpc += 4;
536 #elif defined(__linux__) && defined(__sparc__)
537     /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
538     struct sigcontext *sc = (struct sigcontext*)puc;
539     pc = sc->si_regs.pc;
540     sc->si_regs.pc = sc->si_regs.npc;
541     sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4;
542 #elif defined(__linux__) && defined(__powerpc__)
543 #if defined(__powerpc64__)
544     mcontext_t *mc = &uc->uc_mcontext;
545     unsigned long *regs = &mc->gp_regs[0];
546 #else
547     mcontext_t *mc = uc->uc_mcontext.uc_regs;
548     unsigned long *regs = &mc->gregs[0];
549 #endif
550     pc = regs[PT_NIP];
551     regs[PT_NIP] += 4;
552     regs[PT_FPSCR] = 0x80|0x40|0x10;	/* VE, OE, ZE; not UE or XE */
553 #elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__))
554 # error "Floating-point exceptions not supported on MacOS X"
555 #elif defined(__DARWIN__) && defined(__ppc__)
556     mcontext_t mc = uc->uc_mcontext;
557     pc = mc->ss.srr0;
558     mc->ss.srr0 += 4;
559     mc->fs.fpscr = 0x80|0x40|0x10;
560 #elif defined(__FreeBSD__) && defined(__x86_64__)
561     mcontext_t *mc = &uc->uc_mcontext;
562     struct savefpu *savefpu = (struct savefpu*)&mc->mc_fpstate;
563     struct envxmm *envxmm = &savefpu->sv_env;
564     pc = mc_pc(mc);
565     envxmm->en_mxcsr = 0x1F80;
566     envxmm->en_sw &= ~0xFF;
567 #elif defined(__FreeBSD__) && defined(__i386__)
568     mcontext_t *mc = &uc->uc_mcontext;
569     union savefpu *savefpu = (union savefpu*)&mc->mc_fpstate;
570     pc = mc_pc(mc);
571     if (mc->mc_fpformat == _MC_FPFMT_XMM) {
572 	struct envxmm *envxmm = &savefpu->sv_xmm.sv_env;
573 	envxmm->en_mxcsr = 0x1F80;
574 	envxmm->en_sw &= ~0xFF;
575     } else {
576 	struct env87 *env87 = &savefpu->sv_87.sv_env;
577 	env87->en_sw &= ~0xFF;
578     }
579 #elif defined(__NetBSD__) && defined(__x86_64__)
580     mcontext_t *mc = &uc->uc_mcontext;
581     struct fxsave64 *fxsave = (struct fxsave64 *)&mc->__fpregs;
582     pc = mc_pc(mc);
583     fxsave->fx_mxcsr = 0x1F80;
584     fxsave->fx_fsw &= ~0xFF;
585 #elif defined(__NetBSD__) && defined(__i386__)
586     mcontext_t *mc = &uc->uc_mcontext;
587     pc = mc_pc(mc);
588     if (uc->uc_flags & _UC_FXSAVE) {
589 	struct envxmm *envxmm = (struct envxmm *)&mc->__fpregs;
590 	envxmm->en_mxcsr = 0x1F80;
591 	envxmm->en_sw &= ~0xFF;
592     } else {
593 	struct env87 *env87 = (struct env87 *)&mc->__fpregs;
594 	env87->en_sw &= ~0xFF;
595     }
596 #elif defined(__OpenBSD__) && defined(__x86_64__)
597     struct fxsave64 *fxsave = uc->sc_fpstate;
598     pc = mc_pc(uc);
599     fxsave->fx_mxcsr = 0x1F80;
600     fxsave->fx_fsw &= ~0xFF;
601 #elif defined(__sun__) && defined(__x86_64__)
602     mcontext_t *mc = &uc->uc_mcontext;
603     struct fpchip_state *fpstate = &mc->fpregs.fp_reg_set.fpchip_state;
604     pc = mc_pc(mc);
605     fpstate->mxcsr = 0x1F80;
606     fpstate->sw &= ~0xFF;
607 #endif
608 #if 0
609     {
610 	char buf[128];
611 	snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc);
612 	write(2, buf, strlen(buf));
613     }
614 #endif
615     set_current_fp_exception(pc);
616 }
617 
erts_thread_catch_fp_exceptions(void)618 static void erts_thread_catch_fp_exceptions(void)
619 {
620     struct sigaction act;
621     memset(&act, 0, sizeof act);
622     act.sa_sigaction = fpe_sig_action;
623     act.sa_flags = SA_SIGINFO;
624     sigaction(SIGFPE, &act, NULL);
625     unmask_fpe();
626 }
627 
628 #else  /* !((__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
629 
fpe_sig_handler(int sig)630 static void fpe_sig_handler(int sig)
631 {
632     set_current_fp_exception(1); /* XXX: convert to sigaction so we can get the trap PC */
633 }
634 
erts_thread_catch_fp_exceptions(void)635 static void erts_thread_catch_fp_exceptions(void)
636 {
637     sys_signal(SIGFPE, fpe_sig_handler);
638     unmask_fpe();
639 }
640 
641 #endif /* (__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
642 
643 /* once-only initialisation early in the main thread */
erts_sys_init_float(void)644 void erts_sys_init_float(void)
645 {
646     erts_init_fp_exception();
647     erts_thread_catch_fp_exceptions();
648     erts_printf_block_fpe = erts_sys_block_fpe;
649     erts_printf_unblock_fpe = erts_sys_unblock_fpe;
650 }
651 
652 #endif /* NO_FPE_SIGNALS */
653 
erts_thread_init_float(void)654 void erts_thread_init_float(void)
655 {
656     /* This allows Erlang schedulers to leave Erlang-process context
657        and still have working FP exceptions. XXX: is this needed? */
658     erts_thread_init_fp_exception();
659 
660 #ifndef NO_FPE_SIGNALS
661     /* NOTE:
662      *  erts_thread_disable_fpe() is called in all threads at
663      *  creation. We at least need to call unmask_fpe()
664      */
665 #if defined(__DARWIN__) || defined(__FreeBSD__)
666     /* Darwin (7.9.0) does not appear to propagate FP exception settings
667        to a new thread from its parent. So if we want FP exceptions, we
668        must manually re-enable them in each new thread.
669        FreeBSD 6.1 appears to suffer from a similar issue. */
670     erts_thread_catch_fp_exceptions();
671 #else
672     unmask_fpe();
673 #endif
674 
675 #endif
676 }
677 
erts_thread_disable_fpe(void)678 void erts_thread_disable_fpe(void)
679 {
680 #if !defined(NO_FPE_SIGNALS)
681     (void)mask_fpe();
682 #endif
683 }
684 
685 #if !defined(NO_FPE_SIGNALS)
erts_sys_block_fpe(void)686 int erts_sys_block_fpe(void)
687 {
688     return mask_fpe();
689 }
690 
erts_sys_unblock_fpe(int unmasked)691 void erts_sys_unblock_fpe(int unmasked)
692 {
693     if (unmasked)
694 	unmask_fpe();
695 }
696 #endif
697 
698 /* The following check is incorporated from the Vee machine */
699 
700 #define ISDIGIT(d) ((d) >= '0' && (d) <= '9')
701 
702 /*
703  ** Convert a double to ascii format 0.dddde[+|-]ddd
704  ** return number of characters converted or -1 if error.
705  **
706  ** These two functions should maybe use localeconv() to pick up
707  ** the current radix character, but since it is uncertain how
708  ** expensive such a system call is, and since no-one has heard
709  ** of other radix characters than '.' and ',' an ad-hoc
710  ** low execution time solution is used instead.
711  */
712 
713 int
sys_double_to_chars_ext(double fp,char * buffer,size_t buffer_size,size_t decimals)714 sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t decimals)
715 {
716     char *s = buffer;
717 
718     if (erts_snprintf(buffer, buffer_size, "%.*e", decimals, fp) >= buffer_size)
719         return -1;
720     /* Search upto decimal point */
721     if (*s == '+' || *s == '-') s++;
722     while (ISDIGIT(*s)) s++;
723     if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */
724     /* Scan to end of string */
725     while (*s) s++;
726     return s-buffer; /* i.e strlen(buffer) */
727 }
728 
729 /* Float conversion */
730 
731 int
sys_chars_to_double(char * buf,double * fp)732 sys_chars_to_double(char* buf, double* fp)
733 {
734 #ifndef NO_FPE_SIGNALS
735     volatile unsigned long *fpexnp = erts_get_current_fp_exception();
736 #endif
737     char *s = buf, *t, *dp;
738 
739     /* Robert says that something like this is what he really wanted:
740      * (The [.,] radix test is NOT what Robert wanted - it was added later)
741      *
742      * 7 == sscanf(Tbuf, "%[+-]%[0-9][.,]%[0-9]%[eE]%[+-]%[0-9]%s", ....);
743      * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7)
744      *   break;
745      */
746 
747     /* Scan string to check syntax. */
748     if (*s == '+' || *s == '-') s++;
749     if (!ISDIGIT(*s))		/* Leading digits. */
750       return -1;
751     while (ISDIGIT(*s)) s++;
752     if (*s != '.' && *s != ',')	/* Decimal part. */
753       return -1;
754     dp = s++;			/* Remember decimal point pos just in case */
755     if (!ISDIGIT(*s))
756       return -1;
757     while (ISDIGIT(*s)) s++;
758     if (*s == 'e' || *s == 'E') {
759 	/* There is an exponent. */
760 	s++;
761 	if (*s == '+' || *s == '-') s++;
762 	if (!ISDIGIT(*s))
763 	  return -1;
764 	while (ISDIGIT(*s)) s++;
765     }
766     if (*s)			/* That should be it */
767       return -1;
768 
769 #ifdef NO_FPE_SIGNALS
770     errno = 0;
771 #endif
772     __ERTS_FP_CHECK_INIT(fpexnp);
773     *fp = strtod(buf, &t);
774     __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
775     if (t != s) {		/* Whole string not scanned */
776 	/* Try again with other radix char */
777 	*dp = (*dp == '.') ? ',' : '.';
778 	errno = 0;
779 	__ERTS_FP_CHECK_INIT(fpexnp);
780 	*fp = strtod(buf, &t);
781 	__ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
782     }
783 
784 #ifdef NO_FPE_SIGNALS
785     if (errno == ERANGE) {
786 	if (*fp == HUGE_VAL || *fp == -HUGE_VAL) {
787 	    /* overflow, should give error */
788 	    return -1;
789 	} else if (t == s && *fp == 0.0) {
790 	    /* This should give 0.0 - OTP-7178 */
791 	    errno = 0;
792 
793 	} else if (*fp == 0.0) {
794 	    return -1;
795 	}
796     }
797 #endif
798     return 0;
799 }
800 
801 #ifdef USE_MATHERR
802 
803 int
matherr(struct exception * exc)804 matherr(struct exception *exc)
805 {
806     return 1;
807 }
808 
809 #endif
810