| /* A part of the Windows CE C Extra Library (celibc) |
| Copyright 2007-2015 PC GO Ld. |
| |
| This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| */ |
| |
| #include "lock.h" |
| #include <stdio.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <windows.h> |
| #include <winioctl.h> |
| |
| #ifndef IOCTL_CONSOLE_SETCONTROLCHANDLER |
| #define IOCTL_CONSOLE_SETCONTROLCHANDLER CTL_CODE(FILE_DEVICE_CONSOLE, 8, METHOD_BUFFERED, FILE_ANY_ACCESS) |
| #endif |
| |
| #define _lock(n) |
| #define _unlock(n) |
| #define MAX_TRIES 10 |
| |
| static __p_sig_fn_t ctrlc_action = SIG_DFL; /* SIGINT */ |
| //static __p_sig_fn_t ctrlbreak_action = SIG_DFL; /* SIGBREAK */ |
| static __p_sig_fn_t abort_action = SIG_DFL; /* SIGABRT */ |
| static __p_sig_fn_t term_action = SIG_DFL; /* SIGTERM */ |
| |
| struct _XCPT_ACTION { |
| |
| /* |
| * exception code or number. defined by the host OS. |
| */ |
| unsigned long XcptNum; |
| |
| /* |
| * signal code or number. defined by the C runtime. |
| */ |
| int SignalNumber; |
| |
| /* |
| * exception action code. either a special code or the address of |
| * a handler function. always determines how the exception filter |
| * should dispose of the exception. |
| */ |
| __p_sig_fn_t XcptAction; |
| }; |
| |
| static struct _XCPT_ACTION _XcptActTab[] = { |
| |
| /* |
| * Exceptions corresponding to the same signal (e.g., SIGFPE) must be grouped |
| * together. |
| * |
| * XcptNum SigNumber XcptAction |
| * ------------------------------------------------------------------- |
| */ |
| { (unsigned long)STATUS_ACCESS_VIOLATION, SIGSEGV, SIG_DFL }, |
| |
| { (unsigned long)STATUS_ILLEGAL_INSTRUCTION, SIGILL, SIG_DFL }, |
| |
| { (unsigned long)STATUS_PRIVILEGED_INSTRUCTION, SIGILL, SIG_DFL }, |
| |
| /* { (unsigned long)STATUS_NONCONTINUABLE_EXCEPTION, NOSIG, SIG_DIE }, |
| */ |
| /* { (unsigned long)STATUS_INVALID_DISPOSITION, NOSIG, SIG_DIE }, |
| */ |
| { (unsigned long)STATUS_FLOAT_DENORMAL_OPERAND, SIGFPE, SIG_DFL }, |
| |
| { (unsigned long)STATUS_FLOAT_DIVIDE_BY_ZERO, SIGFPE, SIG_DFL }, |
| |
| { (unsigned long)STATUS_FLOAT_INEXACT_RESULT, SIGFPE, SIG_DFL }, |
| |
| { (unsigned long)STATUS_FLOAT_INVALID_OPERATION, SIGFPE, SIG_DFL }, |
| |
| { (unsigned long)STATUS_FLOAT_OVERFLOW, SIGFPE, SIG_DFL }, |
| |
| { (unsigned long)STATUS_FLOAT_STACK_CHECK, SIGFPE, SIG_DFL }, |
| |
| { (unsigned long)STATUS_FLOAT_UNDERFLOW, SIGFPE, SIG_DFL }, |
| |
| /* { (unsigned long)STATUS_INTEGER_DIVIDE_BY_ZERO, NOSIG, SIG_DIE }, |
| */ |
| /* { (unsigned long)STATUS_STACK_OVERFLOW, NOSIG, SIG_DIE } |
| */ |
| }; |
| |
| static int _XcptActTabCount; |
| static void * _pxcptinfoptrs = NULL; |
| |
| static struct _XCPT_ACTION * __cdecl siglookup(int sig) { |
| struct _XCPT_ACTION *pxcptact = _XcptActTab; |
| |
| /* |
| * walk thru the _xcptactab table looking for the proper entry. note |
| * that in the case where more than one exception corresponds to the |
| * same signal, the first such instance in the table is the one |
| * returned. |
| */ |
| |
| while(pxcptact->SignalNumber != sig && ++pxcptact < _XcptActTab + _XcptActTabCount); |
| |
| |
| if(pxcptact < _XcptActTab + _XcptActTabCount && pxcptact->SignalNumber == sig) { |
| /* |
| * found a table entry corresponding to the signal |
| */ |
| return pxcptact; |
| } |
| |
| /* |
| * found no table entry corresponding to the signal |
| */ |
| return NULL; |
| } |
| |
| __p_sig_fn_t signal(int sig, __p_sig_fn_t act) { |
| unsigned long int ioctlrsize; |
| struct _XCPT_ACTION *pxcptact; |
| __p_sig_fn_t oldsigact = 0; |
| |
| /* |
| * Check for values of sigact supported on other platforms but not |
| * on this one. Also, make sure sigact is not SIG_DIE |
| */ |
| if(act == SIG_ACK || act == SIG_SGE) goto sigreterror; |
| |
| /* |
| * Take care of all signals which do not correspond to exceptions |
| * in the host OS. Those are: |
| * |
| * SIGINT |
| * SIGABRT |
| * SIGTERM |
| * |
| */ |
| if(sig == SIGINT || sig == SIGABRT || sig == SIGTERM) { |
| _lock(_SIGNAL_LOCK); |
| |
| switch(sig) { |
| case SIGINT: |
| oldsigact = ctrlc_action; |
| ctrlc_action = act; |
| DeviceIoControl((void *)STDIN_FILENO, IOCTL_CONSOLE_SETCONTROLCHANDLER, |
| act, sizeof act, NULL, 0, &ioctlrsize, NULL); |
| break; |
| |
| case SIGABRT: |
| oldsigact = abort_action; |
| abort_action = act; |
| break; |
| |
| case SIGTERM: |
| oldsigact = term_action; |
| term_action = act; |
| break; |
| } |
| |
| _unlock(_SIGNAL_LOCK); |
| goto sigretok; |
| } |
| |
| /* |
| * If we reach here, SignalNumber is supposed to be one the signals which |
| * correspond to exceptions in the host OS. Those are: |
| * |
| * SIGFPE |
| * SIGILL |
| * SIGSEGV |
| */ |
| |
| /* |
| * Make sure SignalNumber is one of the remaining supported signals. |
| */ |
| if(sig != SIGFPE && sig != SIGILL && sig != SIGSEGV) goto sigreterror; |
| |
| |
| /* |
| * look up the proper entry in the exception-action table. note that |
| * if several exceptions are mapped to the same signal, this returns |
| * the pointer to first such entry in the exception action table. it |
| * is assumed that the other entries immediately follow this one. |
| */ |
| if(!(pxcptact = siglookup(sig))) goto sigreterror; |
| |
| /* |
| * SIGSEGV, SIGILL and SIGFPE all have more than one exception mapped |
| * to them. the code below depends on the exceptions corresponding to |
| * the same signal being grouped together in the exception-action |
| * table. |
| */ |
| |
| /* |
| * store old signal action code for return value |
| */ |
| oldsigact = pxcptact->XcptAction; |
| |
| /* |
| * loop through all entries corresponding to the |
| * given signal and update the SigAction and XcptAction |
| * fields as appropriate |
| */ |
| while(pxcptact->SignalNumber == sig) { |
| /* |
| * take care of the SIG_IGN and SIG_DFL action |
| * codes |
| */ |
| pxcptact->XcptAction = act; |
| |
| /* |
| * make sure we don't run off the end of the table |
| */ |
| if(++pxcptact >= _XcptActTab + _XcptActTabCount) break; |
| } |
| |
| sigretok: |
| return oldsigact; |
| |
| sigreterror: |
| errno = EINVAL; |
| return SIG_ERR; |
| } |
| |
| int raise(int sig) { |
| __p_sig_fn_t sigact; |
| __p_sig_fn_t *psigact; |
| PEXCEPTION_POINTERS oldpxcptinfoptrs; |
| |
| switch (sig) { |
| case SIGINT: |
| sigact = *(psigact = &ctrlc_action); |
| break; |
| |
| case SIGABRT: |
| sigact = *(psigact = &abort_action); |
| break; |
| |
| case SIGTERM: |
| sigact = *(psigact = &term_action); |
| break; |
| |
| case SIGFPE: |
| case SIGILL: |
| case SIGSEGV: |
| sigact = *(psigact = &(siglookup( sig )->XcptAction)); |
| break; |
| |
| default: |
| /* |
| * unsupported signal, return an error |
| */ |
| errno = EINVAL; |
| return -1; |
| } |
| |
| |
| /* |
| * If the current action is SIG_IGN, just return |
| */ |
| if(sigact == SIG_IGN) return 0; |
| |
| /* |
| * If the current action is SIG_DFL, take the default action |
| */ |
| if(sigact == SIG_DFL) { |
| |
| /* |
| * The current default action for all of the supported |
| * signals is to terminate with an exit code of 3. |
| */ |
| _exit(3); |
| } |
| |
| /* |
| * From here on, sigact is assumed to be a pointer to a user-supplied |
| * handler. |
| */ |
| |
| /* |
| * For signals which correspond to exceptions, set the pointer |
| * to the EXCEPTION_POINTERS structure to NULL |
| */ |
| if(sig == SIGFPE || sig == SIGSEGV || sig == SIGILL) { |
| oldpxcptinfoptrs = _pxcptinfoptrs; |
| _pxcptinfoptrs = NULL; |
| } |
| |
| /* |
| * Reset the action to SIG_DFL and call the user specified handler |
| * routine. |
| */ |
| sigact(sig); |
| |
| /* |
| * For signals which correspond to exceptions, restore the pointer |
| * to the EXCEPTION_POINTERS structure. |
| */ |
| if(sig == SIGFPE || sig == SIGSEGV || sig == SIGILL) { |
| _pxcptinfoptrs = oldpxcptinfoptrs; |
| /* |
| * If sig is SIGFPE, also restore _fpecode |
| */ |
| } |
| return 0; |
| } |