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