1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 /*
  26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  27  * Use is subject to license terms.
  28  */
  29 
  30 #include "fenv_synonyms.h"
  31 #undef lint
  32 #include <signal.h>
  33 #include <siginfo.h>
  34 #include <ucontext.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <unistd.h>
  38 #include <thread.h>
  39 #include <math.h>
  40 #if defined(__SUNPRO_C)
  41 #include <sunmath.h>
  42 #endif
  43 #include <fenv.h>
  44 #include "fex_handler.h"
  45 #include "fenv_inlines.h"
  46 
  47 #if defined(__sparc) && !defined(__sparcv9)
  48 #include <sys/procfs.h>
  49 #endif
  50 
  51 /* 2.x signal.h doesn't declare sigemptyset or sigismember
  52    if they're #defined (see sys/signal.h) */
  53 extern int sigemptyset(sigset_t *);
  54 extern int sigismember(const sigset_t *, int);
  55 
  56 /* external globals */
  57 void (*__mt_fex_sync)() = NULL; /* for synchronization with libmtsk */
  58 #pragma weak __mt_fex_sync
  59 
  60 #ifdef LIBM_MT_FEX_SYNC
  61 void (*__libm_mt_fex_sync)() = NULL; /* new, improved version of above */
  62 #pragma weak __libm_mt_fex_sync
  63 #endif
  64 
  65 /* private variables */
  66 static fex_handler_t main_handlers;
  67 static int handlers_initialized = 0;
  68 static thread_key_t handlers_key;
  69 static mutex_t handlers_key_lock = DEFAULTMUTEX;
  70 
  71 static struct sigaction oact = { 0, SIG_DFL };
  72 static mutex_t hdlr_lock = DEFAULTMUTEX;
  73 static int hdlr_installed = 0;
  74 
  75 /* private const data */
  76 static const int te_bit[FEX_NUM_EXC] = {
  77         1 << fp_trap_inexact,
  78         1 << fp_trap_division,
  79         1 << fp_trap_underflow,
  80         1 << fp_trap_overflow,
  81         1 << fp_trap_invalid,
  82         1 << fp_trap_invalid,
  83         1 << fp_trap_invalid,
  84         1 << fp_trap_invalid,
  85         1 << fp_trap_invalid,
  86         1 << fp_trap_invalid,
  87         1 << fp_trap_invalid,
  88         1 << fp_trap_invalid
  89 };
  90 
  91 /*
  92 *  Return the traps to be enabled given the current handling modes
  93 *  and flags
  94 */
  95 static int
  96 __fex_te_needed(struct fex_handler_data *thr_handlers, unsigned long fsr)
  97 {
  98         int             i, ex, te;
  99 
 100         /* set traps for handling modes */
 101         te = 0;
 102         for (i = 0; i < FEX_NUM_EXC; i++)
 103                 if (thr_handlers[i].__mode != FEX_NONSTOP)
 104                         te |= te_bit[i];
 105 
 106         /* add traps for retrospective diagnostics */
 107         if (fex_get_log()) {
 108                 ex = (int)__fenv_get_ex(fsr);
 109                 if (!(ex & FE_INEXACT))
 110                         te |= (1 << fp_trap_inexact);
 111                 if (!(ex & FE_UNDERFLOW))
 112                         te |= (1 << fp_trap_underflow);
 113                 if (!(ex & FE_OVERFLOW))
 114                         te |= (1 << fp_trap_overflow);
 115                 if (!(ex & FE_DIVBYZERO))
 116                         te |= (1 << fp_trap_division);
 117                 if (!(ex & FE_INVALID))
 118                         te |= (1 << fp_trap_invalid);
 119         }
 120 
 121         return te;
 122 }
 123 
 124 /*
 125 *  The following function synchronizes with libmtsk (SPARC only, for now)
 126 */
 127 static void
 128 __fex_sync_with_libmtsk(int begin, int master)
 129 {
 130         static fenv_t master_env;
 131         static int env_initialized = 0;
 132         static mutex_t env_lock = DEFAULTMUTEX;
 133 
 134         if (begin) {
 135                 mutex_lock(&env_lock);
 136                 if (master) {
 137                         (void) fegetenv(&master_env);
 138                         env_initialized = 1;
 139                 }
 140                 else if (env_initialized)
 141                         (void) fesetenv(&master_env);
 142                 mutex_unlock(&env_lock);
 143         }
 144         else if (master && fex_get_log())
 145                 __fex_update_te();
 146 }
 147 
 148 #ifdef LIBM_MT_FEX_SYNC
 149 /*
 150 *  The following function may be used for synchronization with any
 151 *  internal project that manages multiple threads
 152 */
 153 enum __libm_mt_fex_sync_actions {
 154         __libm_mt_fex_start_master = 0,
 155         __libm_mt_fex_start_slave,
 156         __libm_mt_fex_finish_master,
 157         __libm_mt_fex_finish_slave
 158 };
 159 
 160 struct __libm_mt_fex_sync_data {
 161         fenv_t  master_env;
 162         int             initialized;
 163         mutex_t lock;
 164 };
 165 
 166 static void
 167 __fex_sync_with_threads(enum __libm_mt_fex_sync_actions action,
 168         struct __libm_mt_fex_sync_data *thr_env)
 169 {
 170         switch (action) {
 171         case __libm_mt_fex_start_master:
 172                 mutex_lock(&thr_env->lock);
 173                 (void) fegetenv(&thr_env->master_env);
 174                 thr_env->initialized = 1;
 175                 mutex_unlock(&thr_env->lock);
 176                 break;
 177 
 178         case __libm_mt_fex_start_slave:
 179                 mutex_lock(&thr_env->lock);
 180                 if (thr_env->initialized)
 181                         (void) fesetenv(&thr_env->master_env);
 182                 mutex_unlock(&thr_env->lock);
 183                 break;
 184 
 185         case __libm_mt_fex_finish_master:
 186 #if defined(__x86)
 187                 __fex_update_te();
 188 #else
 189                 if (fex_get_log())
 190                         __fex_update_te();
 191 #endif
 192                 break;
 193 
 194         case __libm_mt_fex_finish_slave:
 195 #if defined(__x86)
 196                 /* clear traps, making all accrued flags visible in status word */
 197                 {
 198                         unsigned long   fsr;
 199                         __fenv_getfsr(&fsr);
 200                         __fenv_set_te(fsr, 0);
 201                         __fenv_setfsr(&fsr);
 202                 }
 203 #endif
 204                 break;
 205         }
 206 }
 207 #endif
 208 
 209 #if defined(__sparc)
 210 
 211 /*
 212 *  Code for setting or clearing interval mode on US-III and above.
 213 *  This is embedded as data so we don't have to mark the library
 214 *  as a v8plusb/v9b object.  (I could have just used one entry and
 215 *  modified the second word to set the bits I want, but that would
 216 *  have required another mutex.)
 217 */
 218 static const unsigned int siam[][2] = {
 219         { 0x81c3e008, 0x81b01020 }, /* retl, siam 0 */
 220         { 0x81c3e008, 0x81b01024 }, /* retl, siam 4 */
 221         { 0x81c3e008, 0x81b01025 }, /* retl, siam 5 */
 222         { 0x81c3e008, 0x81b01026 }, /* retl, siam 6 */
 223         { 0x81c3e008, 0x81b01027 }  /* retl, siam 7 */
 224 };
 225 
 226 /*
 227 *  If a handling mode is in effect, apply it; otherwise invoke the
 228 *  saved handler
 229 */
 230 static void
 231 __fex_hdlr(int sig, siginfo_t *sip, ucontext_t *uap)
 232 {
 233         struct fex_handler_data *thr_handlers;
 234         struct sigaction        act;
 235         void                    (*handler)(), (*siamp)();
 236         int                     mode, i;
 237         enum fex_exception      e;
 238         fex_info_t              info;
 239         unsigned long           fsr, tmpfsr, addr;
 240         unsigned int            gsr;
 241 
 242         /* determine which exception occurred */
 243         switch (sip->si_code) {
 244         case FPE_FLTDIV:
 245                 e = fex_division;
 246                 break;
 247         case FPE_FLTOVF:
 248                 e = fex_overflow;
 249                 break;
 250         case FPE_FLTUND:
 251                 e = fex_underflow;
 252                 break;
 253         case FPE_FLTRES:
 254                 e = fex_inexact;
 255                 break;
 256         case FPE_FLTINV:
 257                 if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
 258                         goto not_ieee;
 259                 break;
 260         default:
 261                 /* not an IEEE exception */
 262                 goto not_ieee;
 263         }
 264 
 265         /* get the handling mode */
 266         mode = FEX_NOHANDLER;
 267         handler = oact.sa_handler; /* for log; just looking, no need to lock */
 268         thr_handlers = __fex_get_thr_handlers();
 269         if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
 270                 mode = thr_handlers[(int)e].__mode;
 271                 handler = thr_handlers[(int)e].__handler;
 272         }
 273 
 274         /* make an entry in the log of retro. diag. if need be */
 275         i = ((int)uap->uc_mcontext.fpregs.fpu_fsr >> 5) & 0x1f;
 276         __fex_mklog(uap, (char *)sip->si_addr, i, e, mode, (void *)handler);
 277 
 278         /* handle the exception based on the mode */
 279         if (mode == FEX_NOHANDLER)
 280                 goto not_ieee;
 281         else if (mode == FEX_ABORT)
 282                 abort();
 283         else if (mode == FEX_SIGNAL) {
 284                 handler(sig, sip, uap);
 285                 return;
 286         }
 287 
 288         /* custom or nonstop mode; disable traps and clear flags */
 289         __fenv_getfsr(&fsr);
 290         __fenv_set_te(fsr, 0);
 291         __fenv_set_ex(fsr, 0);
 292 
 293         /* if interval mode was set, clear it, then substitute the
 294            interval rounding direction and clear ns mode in the fsr */
 295 #ifdef __sparcv9
 296         gsr = uap->uc_mcontext.asrs[3];
 297 #else
 298         gsr = 0;
 299         if (uap->uc_mcontext.xrs.xrs_id == XRS_ID)
 300                 gsr = (*(unsigned long long*)((prxregset_t*)uap->uc_mcontext.
 301                     xrs.xrs_ptr)->pr_un.pr_v8p.pr_filler);
 302 #endif
 303         gsr = (gsr >> 25) & 7;
 304         if (gsr & 4) {
 305                 siamp = (void (*)()) siam[0];
 306                 siamp();
 307                 tmpfsr = fsr;
 308                 fsr = (fsr & ~0xc0400000ul) | ((gsr & 3) << 30);
 309         }
 310         __fenv_setfsr(&fsr);
 311 
 312         /* decode the operation */
 313         __fex_get_op(sip, uap, &info);
 314 
 315         /* if a custom mode handler is installed, invoke it */
 316         if (mode == FEX_CUSTOM) {
 317                 /* if we got here from feraiseexcept, pass dummy info */
 318                 addr = (unsigned long)sip->si_addr;
 319                 if (addr >= (unsigned long)feraiseexcept &&
 320                     addr < (unsigned long)fetestexcept) {
 321                         info.op = fex_other;
 322                         info.op1.type = info.op2.type = info.res.type =
 323                             fex_nodata;
 324                 }
 325 
 326                 /* restore interval mode if it was set, and put the original
 327                    rounding direction and ns mode back in the fsr */
 328                 if (gsr & 4) {
 329                         __fenv_setfsr(&tmpfsr);
 330                         siamp = (void (*)()) siam[1 + (gsr & 3)];
 331                         siamp();
 332                 }
 333 
 334                 handler(1 << (int)e, &info);
 335 
 336                 /* restore modes in case the user's handler changed them */
 337                 if (gsr & 4) {
 338                         siamp = (void (*)()) siam[0];
 339                         siamp();
 340                 }
 341                 __fenv_setfsr(&fsr);
 342         }
 343 
 344         /* stuff the result */
 345         __fex_st_result(sip, uap, &info);
 346 
 347         /* "or" in any exception flags and update traps */
 348         fsr = uap->uc_mcontext.fpregs.fpu_fsr;
 349         fsr |= ((info.flags & 0x1f) << 5);
 350         i = __fex_te_needed(thr_handlers, fsr);
 351         __fenv_set_te(fsr, i);
 352         uap->uc_mcontext.fpregs.fpu_fsr = fsr;
 353         return;
 354 
 355 not_ieee:
 356         /* revert to the saved handler (if any) */
 357         mutex_lock(&hdlr_lock);
 358         act = oact;
 359         mutex_unlock(&hdlr_lock);
 360         switch ((unsigned long)act.sa_handler) {
 361         case (unsigned long)SIG_DFL:
 362                 /* simulate trap with no handler installed */
 363                 sigaction(SIGFPE, &act, NULL);
 364                 kill(getpid(), SIGFPE);
 365                 break;
 366 #if !defined(__lint)
 367         case (unsigned long)SIG_IGN:
 368                 break;
 369 #endif
 370         default:
 371                 act.sa_handler(sig, sip, uap);
 372         }
 373 }
 374 
 375 #elif defined(__x86)
 376 
 377 #if defined(__amd64)
 378 #define test_sse_hw     1
 379 #else
 380 extern int _sse_hw;
 381 #define test_sse_hw     _sse_hw
 382 #endif
 383 
 384 #if !defined(REG_PC)
 385 #define REG_PC  EIP
 386 #endif
 387 
 388 /*
 389 *  If a handling mode is in effect, apply it; otherwise invoke the
 390 *  saved handler
 391 */
 392 static void
 393 __fex_hdlr(int sig, siginfo_t *sip, ucontext_t *uap)
 394 {
 395         struct fex_handler_data *thr_handlers;
 396         struct sigaction        act;
 397         void                    (*handler)() = NULL, (*simd_handler[4])();
 398         int                     mode, simd_mode[4], i, len, accrued, *ap;
 399         unsigned int            cwsw, oldcwsw, mxcsr, oldmxcsr;
 400         enum fex_exception      e, simd_e[4];
 401         fex_info_t              info, simd_info[4];
 402         unsigned long           addr;
 403         siginfo_t               osip = *sip;
 404         sseinst_t               inst;
 405 
 406         /* check for an exception caused by an SSE instruction */
 407         if (!(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status & 0x80)) {
 408                 len = __fex_parse_sse(uap, &inst);
 409                 if (len == 0)
 410                         goto not_ieee;
 411 
 412                 /* disable all traps and clear flags */
 413                 __fenv_getcwsw(&oldcwsw);
 414                 cwsw = (oldcwsw & ~0x3f) | 0x003f0000;
 415                 __fenv_setcwsw(&cwsw);
 416                 __fenv_getmxcsr(&oldmxcsr);
 417                 mxcsr = (oldmxcsr & ~0x3f) | 0x1f80;
 418                 __fenv_setmxcsr(&mxcsr);
 419 
 420                 if ((int)inst.op & SIMD) {
 421                         __fex_get_simd_op(uap, &inst, simd_e, simd_info);
 422 
 423                         thr_handlers = __fex_get_thr_handlers();
 424                         addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
 425                         accrued = uap->uc_mcontext.fpregs.fp_reg_set.
 426                             fpchip_state.mxcsr;
 427 
 428                         e = (enum fex_exception)-1;
 429                         mode = FEX_NONSTOP;
 430                         for (i = 0; i < 4; i++) {
 431                                 if ((int)simd_e[i] < 0)
 432                                         continue;
 433 
 434                                 e = simd_e[i];
 435                                 simd_mode[i] = FEX_NOHANDLER;
 436                                 simd_handler[i] = oact.sa_handler;
 437                                 if (thr_handlers &&
 438                                     thr_handlers[(int)e].__mode !=
 439                                     FEX_NOHANDLER) {
 440                                         simd_mode[i] =
 441                                             thr_handlers[(int)e].__mode;
 442                                         simd_handler[i] =
 443                                             thr_handlers[(int)e].__handler;
 444                                 }
 445                                 accrued &= ~te_bit[(int)e];
 446                                 switch (simd_mode[i]) {
 447                                 case FEX_ABORT:
 448                                         mode = FEX_ABORT;
 449                                         break;
 450                                 case FEX_SIGNAL:
 451                                         if (mode != FEX_ABORT)
 452                                                 mode = FEX_SIGNAL;
 453                                         handler = simd_handler[i];
 454                                         break;
 455                                 case FEX_NOHANDLER:
 456                                         if (mode != FEX_ABORT && mode !=
 457                                             FEX_SIGNAL)
 458                                                 mode = FEX_NOHANDLER;
 459                                         break;
 460                                 }
 461                         }
 462                         if (e == (enum fex_exception)-1) {
 463                                 __fenv_setcwsw(&oldcwsw);
 464                                 __fenv_setmxcsr(&oldmxcsr);
 465                                 goto not_ieee;
 466                         }
 467                         accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
 468                             fpchip_state.status;
 469                         ap = __fex_accrued();
 470                         accrued |= *ap;
 471                         accrued &= 0x3d;
 472 
 473                         for (i = 0; i < 4; i++) {
 474                                 if ((int)simd_e[i] < 0)
 475                                         continue;
 476 
 477                                 __fex_mklog(uap, (char *)addr, accrued,
 478                                     simd_e[i], simd_mode[i],
 479                                     (void *)simd_handler[i]);
 480                         }
 481 
 482                         if (mode == FEX_NOHANDLER) {
 483                                 __fenv_setcwsw(&oldcwsw);
 484                                 __fenv_setmxcsr(&oldmxcsr);
 485                                 goto not_ieee;
 486                         } else if (mode == FEX_ABORT) {
 487                                 abort();
 488                         } else if (mode == FEX_SIGNAL) {
 489                                 __fenv_setcwsw(&oldcwsw);
 490                                 __fenv_setmxcsr(&oldmxcsr);
 491                                 handler(sig, &osip, uap);
 492                                 return;
 493                         }
 494 
 495                         *ap = 0;
 496                         for (i = 0; i < 4; i++) {
 497                                 if ((int)simd_e[i] < 0)
 498                                         continue;
 499 
 500                                 if (simd_mode[i] == FEX_CUSTOM) {
 501                                         handler(1 << (int)simd_e[i],
 502                                             &simd_info[i]);
 503                                         __fenv_setcwsw(&cwsw);
 504                                         __fenv_setmxcsr(&mxcsr);
 505                                 }
 506                         }
 507 
 508                         __fex_st_simd_result(uap, &inst, simd_e, simd_info);
 509                         for (i = 0; i < 4; i++) {
 510                                 if ((int)simd_e[i] < 0)
 511                                         continue;
 512 
 513                                 accrued |= simd_info[i].flags;
 514                         }
 515 
 516                         if ((int)inst.op & INTREG) {
 517                                 /* set MMX mode */
 518 #if defined(__amd64)
 519                                 uap->uc_mcontext.fpregs.fp_reg_set.
 520                                     fpchip_state.sw &= ~0x3800;
 521                                 uap->uc_mcontext.fpregs.fp_reg_set.
 522                                     fpchip_state.fctw = 0;
 523 #else
 524                                 uap->uc_mcontext.fpregs.fp_reg_set.
 525                                     fpchip_state.state[1] &= ~0x3800;
 526                                 uap->uc_mcontext.fpregs.fp_reg_set.
 527                                     fpchip_state.state[2] = 0;
 528 #endif
 529                         }
 530                 } else {
 531                         e = __fex_get_sse_op(uap, &inst, &info);
 532                         if ((int)e < 0) {
 533                                 __fenv_setcwsw(&oldcwsw);
 534                                 __fenv_setmxcsr(&oldmxcsr);
 535                                 goto not_ieee;
 536                         }
 537 
 538                         mode = FEX_NOHANDLER;
 539                         handler = oact.sa_handler;
 540                         thr_handlers = __fex_get_thr_handlers();
 541                         if (thr_handlers && thr_handlers[(int)e].__mode !=
 542                             FEX_NOHANDLER) {
 543                                 mode = thr_handlers[(int)e].__mode;
 544                                 handler = thr_handlers[(int)e].__handler;
 545                         }
 546 
 547                         addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
 548                         accrued = uap->uc_mcontext.fpregs.fp_reg_set.
 549                             fpchip_state.mxcsr & ~te_bit[(int)e];
 550                         accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
 551                             fpchip_state.status;
 552                         ap = __fex_accrued();
 553                         accrued |= *ap;
 554                         accrued &= 0x3d;
 555                         __fex_mklog(uap, (char *)addr, accrued, e, mode,
 556                             (void *)handler);
 557 
 558                         if (mode == FEX_NOHANDLER) {
 559                                 __fenv_setcwsw(&oldcwsw);
 560                                 __fenv_setmxcsr(&oldmxcsr);
 561                                 goto not_ieee;
 562                         } else if (mode == FEX_ABORT) {
 563                                 abort();
 564                         } else if (mode == FEX_SIGNAL) {
 565                                 __fenv_setcwsw(&oldcwsw);
 566                                 __fenv_setmxcsr(&oldmxcsr);
 567                                 handler(sig, &osip, uap);
 568                                 return;
 569                         } else if (mode == FEX_CUSTOM) {
 570                                 *ap = 0;
 571                                 if (addr >= (unsigned long)feraiseexcept &&
 572                                     addr < (unsigned long)fetestexcept) {
 573                                         info.op = fex_other;
 574                                         info.op1.type = info.op2.type =
 575                                             info.res.type = fex_nodata;
 576                                 }
 577                                 handler(1 << (int)e, &info);
 578                                 __fenv_setcwsw(&cwsw);
 579                                 __fenv_setmxcsr(&mxcsr);
 580                         }
 581 
 582                         __fex_st_sse_result(uap, &inst, e, &info);
 583                         accrued |= info.flags;
 584 
 585 #if defined(__amd64)
 586                         /*
 587                          * In 64-bit mode, the 32-bit convert-to-integer
 588                          * instructions zero the upper 32 bits of the
 589                          * destination.  (We do this here and not in
 590                          * __fex_st_sse_result because __fex_st_sse_result
 591                          * can be called from __fex_st_simd_result, too.)
 592                          */
 593                         if (inst.op == cvtss2si || inst.op == cvttss2si ||
 594                             inst.op == cvtsd2si || inst.op == cvttsd2si)
 595                                 inst.op1->i[1] = 0;
 596 #endif
 597                 }
 598 
 599                 /* advance the pc past the SSE instruction */
 600                 uap->uc_mcontext.gregs[REG_PC] += len;
 601                 goto update_state;
 602         }
 603 
 604         /* determine which exception occurred */
 605         __fex_get_x86_exc(sip, uap);
 606         switch (sip->si_code) {
 607         case FPE_FLTDIV:
 608                 e = fex_division;
 609                 break;
 610         case FPE_FLTOVF:
 611                 e = fex_overflow;
 612                 break;
 613         case FPE_FLTUND:
 614                 e = fex_underflow;
 615                 break;
 616         case FPE_FLTRES:
 617                 e = fex_inexact;
 618                 break;
 619         case FPE_FLTINV:
 620                 if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
 621                         goto not_ieee;
 622                 break;
 623         default:
 624                 /* not an IEEE exception */
 625                 goto not_ieee;
 626         }
 627 
 628         /* get the handling mode */
 629         mode = FEX_NOHANDLER;
 630         handler = oact.sa_handler; /* for log; just looking, no need to lock */
 631         thr_handlers = __fex_get_thr_handlers();
 632         if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
 633                 mode = thr_handlers[(int)e].__mode;
 634                 handler = thr_handlers[(int)e].__handler;
 635         }
 636 
 637         /* make an entry in the log of retro. diag. if need be */
 638 #if defined(__amd64)
 639         addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
 640             fpchip_state.rip;
 641 #else
 642         addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
 643             fpchip_state.state[3];
 644 #endif
 645         accrued = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status & 
 646             ~te_bit[(int)e];
 647         if (test_sse_hw)
 648                 accrued |= uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 649                     mxcsr;
 650         ap = __fex_accrued();
 651         accrued |= *ap;
 652         accrued &= 0x3d;
 653         __fex_mklog(uap, (char *)addr, accrued, e, mode, (void *)handler);
 654 
 655         /* handle the exception based on the mode */
 656         if (mode == FEX_NOHANDLER)
 657                 goto not_ieee;
 658         else if (mode == FEX_ABORT)
 659                 abort();
 660         else if (mode == FEX_SIGNAL) {
 661                 handler(sig, &osip, uap);
 662                 return;
 663         }
 664 
 665         /* disable all traps and clear flags */
 666         __fenv_getcwsw(&cwsw);
 667         cwsw = (cwsw & ~0x3f) | 0x003f0000;
 668         __fenv_setcwsw(&cwsw);
 669         if (test_sse_hw) {
 670                 __fenv_getmxcsr(&mxcsr);
 671                 mxcsr = (mxcsr & ~0x3f) | 0x1f80;
 672                 __fenv_setmxcsr(&mxcsr);
 673         }
 674         *ap = 0;
 675 
 676         /* decode the operation */
 677         __fex_get_op(sip, uap, &info);
 678 
 679         /* if a custom mode handler is installed, invoke it */
 680         if (mode == FEX_CUSTOM) {
 681                 /* if we got here from feraiseexcept, pass dummy info */
 682                 if (addr >= (unsigned long)feraiseexcept &&
 683                     addr < (unsigned long)fetestexcept) {
 684                         info.op = fex_other;
 685                         info.op1.type = info.op2.type = info.res.type =
 686                             fex_nodata;
 687                 }
 688 
 689                 handler(1 << (int)e, &info);
 690 
 691                 /* restore modes in case the user's handler changed them */
 692                 __fenv_setcwsw(&cwsw);
 693                 if (test_sse_hw)
 694                         __fenv_setmxcsr(&mxcsr);
 695         }
 696 
 697         /* stuff the result */
 698         __fex_st_result(sip, uap, &info);
 699         accrued |= info.flags;
 700 
 701 update_state:
 702         accrued &= 0x3d;
 703         i = __fex_te_needed(thr_handlers, accrued);
 704         *ap = accrued & i;
 705 #if defined(__amd64)
 706         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw &= ~0x3d;
 707         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= (accrued & ~i);
 708         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw |= 0x3d;
 709         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw &= ~i;
 710 #else
 711         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] &= ~0x3d;
 712         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] |=
 713             (accrued & ~i);
 714         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] |= 0x3d;
 715         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] &= ~i;
 716 #endif
 717         if (test_sse_hw) {
 718                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &= ~0x3d;
 719                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr |=
 720                     0x1e80 | (accrued & ~i);
 721                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &=
 722                     ~(i << 7);
 723         }
 724         return;
 725 
 726 not_ieee:
 727         /* revert to the saved handler (if any) */
 728         mutex_lock(&hdlr_lock);
 729         act = oact;
 730         mutex_unlock(&hdlr_lock);
 731         switch ((unsigned long)act.sa_handler) {
 732         case (unsigned long)SIG_DFL:
 733                 /* simulate trap with no handler installed */
 734                 sigaction(SIGFPE, &act, NULL);
 735                 kill(getpid(), SIGFPE);
 736                 break;
 737 #if !defined(__lint)
 738         case (unsigned long)SIG_IGN:
 739                 break;
 740 #endif
 741         default:
 742                 act.sa_handler(sig, &osip, uap);
 743         }
 744 }
 745 
 746 #else
 747 #error Unknown architecture
 748 #endif
 749 
 750 /*
 751 *  Return a pointer to the thread-specific handler data, and
 752 *  initialize it if necessary
 753 */
 754 struct fex_handler_data *
 755 __fex_get_thr_handlers()
 756 {
 757         struct fex_handler_data *ptr;
 758         unsigned long                   fsr;
 759         int                                             i, te;
 760 
 761         if (thr_main()) {
 762                 if (!handlers_initialized) {
 763                         /* initialize to FEX_NOHANDLER if trap is enabled,
 764                            FEX_NONSTOP if trap is disabled */
 765                         __fenv_getfsr(&fsr);
 766                         te = (int)__fenv_get_te(fsr);
 767                         for (i = 0; i < FEX_NUM_EXC; i++)
 768                                 main_handlers[i].__mode =
 769                                         ((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
 770                         handlers_initialized = 1;
 771                 }
 772                 return main_handlers;
 773         }
 774         else {
 775                 ptr = NULL;
 776                 mutex_lock(&handlers_key_lock);
 777                 if (thr_getspecific(handlers_key, (void **)&ptr) != 0 &&
 778                         thr_keycreate(&handlers_key, free) != 0) {
 779                         mutex_unlock(&handlers_key_lock);
 780                         return NULL;
 781                 }
 782                 mutex_unlock(&handlers_key_lock);
 783                 if (!ptr) {
 784                         if ((ptr = (struct fex_handler_data *)
 785                                 malloc(sizeof(fex_handler_t))) == NULL) {
 786                                 return NULL;
 787                         }
 788                         if (thr_setspecific(handlers_key, (void *)ptr) != 0) {
 789                                 (void)free(ptr);
 790                                 return NULL;
 791                         }
 792                         /* initialize to FEX_NOHANDLER if trap is enabled,
 793                            FEX_NONSTOP if trap is disabled */
 794                         __fenv_getfsr(&fsr);
 795                         te = (int)__fenv_get_te(fsr);
 796                         for (i = 0; i < FEX_NUM_EXC; i++)
 797                                 ptr[i].__mode = ((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
 798                 }
 799                 return ptr;
 800         }
 801 }
 802 
 803 /*
 804 *  Update the trap enable bits according to the selected modes
 805 */
 806 void
 807 __fex_update_te()
 808 {
 809         struct fex_handler_data *thr_handlers;
 810         struct sigaction                act, tmpact;
 811         sigset_t                                blocked;
 812         unsigned long                   fsr;
 813         int                                             te;
 814 
 815         /* determine which traps are needed */
 816         thr_handlers = __fex_get_thr_handlers();
 817         __fenv_getfsr(&fsr);
 818         te = __fex_te_needed(thr_handlers, fsr);
 819 
 820         /* install __fex_hdlr as necessary */
 821         if (!hdlr_installed && te) {
 822                 act.sa_handler = __fex_hdlr;
 823                 sigemptyset(&act.sa_mask);
 824                 act.sa_flags = SA_SIGINFO;
 825                 sigaction(SIGFPE, &act, &tmpact);
 826                 if (tmpact.sa_handler != __fex_hdlr)
 827                 {
 828                         mutex_lock(&hdlr_lock);
 829                         oact = tmpact;
 830                         mutex_unlock(&hdlr_lock);
 831                 }
 832                 hdlr_installed = 1;
 833         }
 834 
 835         /* set the new trap enable bits (only if SIGFPE is not blocked) */
 836         if (sigprocmask(0, NULL, &blocked) == 0 &&
 837                 !sigismember(&blocked, SIGFPE)) {
 838                 __fenv_set_te(fsr, te);
 839                 __fenv_setfsr(&fsr);
 840         }
 841 
 842         /* synchronize with libmtsk */
 843         __mt_fex_sync = __fex_sync_with_libmtsk;
 844 
 845 #ifdef LIBM_MT_FEX_SYNC
 846         /* synchronize with other projects */
 847         __libm_mt_fex_sync = __fex_sync_with_threads;
 848 #endif
 849 }