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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T 26 * All Rights Reserved 27 * 28 * Portions of this source code were derived from Berkeley 29 * 4.3 BSD under license from the regents of the University of 30 * California. 31 */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 /* Swap handler for SIGFPE codes. */ 36 37 #pragma weak sigfpe = _sigfpe 38 39 #include "synonyms.h" 40 #include <mtlib.h> 41 #include <errno.h> 42 #include <signal.h> 43 #include <floatingpoint.h> 44 #include <sys/types.h> 45 #include <sys/ucontext.h> 46 #include <sys/siginfo.h> 47 #include <thread.h> 48 #include <synch.h> 49 #include <stdlib.h> 50 51 #ifndef FPE_INTDIV 52 #define FPE_INTDIV 1 /* integer divide by zero */ 53 #endif 54 #ifndef FPE_INTOVF 55 #define FPE_INTOVF 2 /* integer overflow */ 56 #endif 57 #ifndef FPE_FLTDIV 58 #define FPE_FLTDIV 3 /* [floating divide by zero] */ 59 #endif 60 #ifndef FPE_FLTOVF 61 #define FPE_FLTOVF 4 /* [floating overflow] */ 62 #endif 63 #ifndef FPE_FLTUND 64 #define FPE_FLTUND 5 /* [floating underflow] */ 65 #endif 66 #ifndef FPE_FLTRES 67 #define FPE_FLTRES 6 /* [floating inexact result] */ 68 #endif 69 #ifndef FPE_FLTINV 70 #define FPE_FLTINV 7 /* [floating invalid operation] */ 71 #endif 72 73 #if defined(__i386) || defined(__amd64) 74 75 #ifndef FPE_FLTSUB 76 #define FPE_FLTSUB 8 /* subscript out of range */ 77 #endif 78 #ifndef FPE_FLTDEN 79 #define FPE_FLTDEN 9 /* x86-specific: denormal operand */ 80 #endif 81 82 #define N_SIGFPE_CODE 10 83 84 #else 85 86 #define N_SIGFPE_CODE 8 87 88 #endif /* __i386 */ 89 90 /* Array of SIGFPE codes. */ 91 92 static const sigfpe_code_type sigfpe_codes[N_SIGFPE_CODE] = { 93 FPE_INTDIV, 94 FPE_INTOVF, 95 FPE_FLTDIV, 96 FPE_FLTOVF, 97 FPE_FLTUND, 98 FPE_FLTRES, 99 FPE_FLTINV, 100 #if defined(__i386) || defined(__amd64) 101 FPE_FLTSUB, 102 FPE_FLTDEN, 103 #endif 104 0 105 }; 106 107 /* Array of handlers. */ 108 109 static mutex_t sigfpe_lock = DEFAULTMUTEX; 110 111 sigfpe_handler_type ieee_handlers[N_IEEE_EXCEPTION]; 112 static sigfpe_handler_type sigfpe_handlers[N_SIGFPE_CODE]; 113 114 static int _sigfpe_master_enabled; 115 /* Originally zero, set to 1 by _enable_sigfpe_master. */ 116 117 #ifndef BADSIG 118 #define BADSIG (void (*)(void))-1 119 #endif 120 121 static void 122 _sigfpe_master(int sig, siginfo_t *siginfo, void *arg) 123 { 124 ucontext_t *ucontext = arg; 125 int i; 126 int code; 127 enum fp_exception_type exception; 128 129 lmutex_lock(&sigfpe_lock); 130 code = siginfo->si_code; 131 for (i = 0; (i < N_SIGFPE_CODE) && (code != sigfpe_codes[i]); i++); 132 /* Find index of handler. */ 133 if (i >= N_SIGFPE_CODE) 134 i = N_SIGFPE_CODE - 1; 135 switch ((intptr_t)sigfpe_handlers[i]) { 136 case ((intptr_t)(SIGFPE_DEFAULT)): 137 switch (code) { 138 case FPE_FLTINV: 139 exception = fp_invalid; 140 goto ieee; 141 case FPE_FLTRES: 142 exception = fp_inexact; 143 goto ieee; 144 case FPE_FLTDIV: 145 exception = fp_division; 146 goto ieee; 147 case FPE_FLTUND: 148 exception = fp_underflow; 149 goto ieee; 150 case FPE_FLTOVF: 151 exception = fp_overflow; 152 goto ieee; 153 default: /* The common default treatment is to abort. */ 154 break; 155 } 156 case ((intptr_t)(SIGFPE_ABORT)): 157 abort(); 158 break; 159 case ((intptr_t)(SIGFPE_IGNORE)): 160 lmutex_unlock(&sigfpe_lock); 161 return; 162 default: /* User-defined not SIGFPE_DEFAULT or SIGFPE_ABORT. */ 163 (sigfpe_handlers[i])(sig, siginfo, ucontext); 164 lmutex_unlock(&sigfpe_lock); 165 return; 166 } 167 ieee: 168 switch ((intptr_t)ieee_handlers[(int)exception]) { 169 case ((intptr_t)(SIGFPE_DEFAULT)): /* Error condition but ignore it. */ 170 case ((intptr_t)(SIGFPE_IGNORE)): /* Error condition but ignore it. */ 171 lmutex_unlock(&sigfpe_lock); 172 return; 173 case ((intptr_t)(SIGFPE_ABORT)): 174 abort(); 175 default: 176 (ieee_handlers[(int)exception])(sig, siginfo, ucontext); 177 lmutex_unlock(&sigfpe_lock); 178 return; 179 } 180 } 181 182 static int 183 _enable_sigfpe_master(void) 184 { 185 /* Enable the sigfpe master handler always. */ 186 struct sigaction newsigact, oldsigact; 187 188 newsigact.sa_sigaction = _sigfpe_master; 189 (void) sigemptyset(&newsigact.sa_mask); 190 newsigact.sa_flags = SA_SIGINFO; /* enhanced handler */ 191 _sigfpe_master_enabled = 1; 192 return (sigaction(SIGFPE, &newsigact, &oldsigact)); 193 } 194 195 static int 196 _test_sigfpe_master(void) 197 { 198 /* 199 * Enable the sigfpe master handler if it's never been enabled 200 * before. 201 */ 202 203 if (_sigfpe_master_enabled == 0) 204 return (_enable_sigfpe_master()); 205 else 206 return (_sigfpe_master_enabled); 207 } 208 209 sigfpe_handler_type 210 sigfpe(sigfpe_code_type code, sigfpe_handler_type hdl) 211 { 212 sigfpe_handler_type oldhdl; 213 int i; 214 215 lmutex_lock(&sigfpe_lock); 216 (void) _test_sigfpe_master(); 217 for (i = 0; (i < N_SIGFPE_CODE) && (code != sigfpe_codes[i]); i++); 218 /* Find index of handler. */ 219 if (i >= N_SIGFPE_CODE) { 220 errno = EINVAL; 221 lmutex_unlock(&sigfpe_lock); 222 /* Not 0 or SIGFPE code */ 223 return ((sigfpe_handler_type)BADSIG); 224 } 225 oldhdl = sigfpe_handlers[i]; 226 sigfpe_handlers[i] = hdl; 227 lmutex_unlock(&sigfpe_lock); 228 return (oldhdl); 229 } 230