blob: eda292fbba3bc47370ce83a81fbbe3a80f137999 [file] [log] [blame] [raw]
/* 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;
}