/*--------------------------------------------------------------------*/ /*--- Signal-related libc stuff. m_libcsignal.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2009 Julian Seward jseward@acm.org 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "pub_core_basics.h" #include "pub_core_debuglog.h" #include "pub_core_vki.h" #include "pub_core_vkiscnums.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_syscall.h" #include "pub_core_libcsignal.h" /* self */ /* sigemptyset, sigfullset, sigaddset and sigdelset return 0 on success and -1 on error. */ /* I believe the indexing scheme in ->sig[] is also correct for 32- and 64-bit AIX (verified 27 July 06). */ Int VG_(sigfillset)( vki_sigset_t* set ) { Int i; if (set == NULL) return -1; for (i = 0; i < _VKI_NSIG_WORDS; i++) set->sig[i] = ~(UWord)0x0; return 0; } Int VG_(sigemptyset)( vki_sigset_t* set ) { Int i; if (set == NULL) return -1; for (i = 0; i < _VKI_NSIG_WORDS; i++) set->sig[i] = 0x0; return 0; } Bool VG_(isemptysigset)( const vki_sigset_t* set ) { Int i; vg_assert(set != NULL); for (i = 0; i < _VKI_NSIG_WORDS; i++) if (set->sig[i] != 0x0) return False; return True; } Bool VG_(isfullsigset)( const vki_sigset_t* set ) { Int i; vg_assert(set != NULL); for (i = 0; i < _VKI_NSIG_WORDS; i++) if (set->sig[i] != ~(UWord)0x0) return False; return True; } Bool VG_(iseqsigset)( const vki_sigset_t* set1, const vki_sigset_t* set2 ) { Int i; vg_assert(set1 != NULL && set2 != NULL); for (i = 0; i < _VKI_NSIG_WORDS; i++) if (set1->sig[i] != set2->sig[i]) return False; return True; } Int VG_(sigaddset)( vki_sigset_t* set, Int signum ) { if (set == NULL) return -1; if (signum < 1 || signum > _VKI_NSIG) return -1; signum--; set->sig[signum / _VKI_NSIG_BPW] |= (1UL << (signum % _VKI_NSIG_BPW)); return 0; } Int VG_(sigdelset)( vki_sigset_t* set, Int signum ) { if (set == NULL) return -1; if (signum < 1 || signum > _VKI_NSIG) return -1; signum--; set->sig[signum / _VKI_NSIG_BPW] &= ~(1UL << (signum % _VKI_NSIG_BPW)); return 0; } Int VG_(sigismember) ( const vki_sigset_t* set, Int signum ) { if (set == NULL) return 0; if (signum < 1 || signum > _VKI_NSIG) return 0; signum--; if (1 & ((set->sig[signum / _VKI_NSIG_BPW]) >> (signum % _VKI_NSIG_BPW))) return 1; else return 0; } /* Add all signals in src to dst. */ void VG_(sigaddset_from_set)( vki_sigset_t* dst, vki_sigset_t* src ) { Int i; vg_assert(dst != NULL && src != NULL); for (i = 0; i < _VKI_NSIG_WORDS; i++) dst->sig[i] |= src->sig[i]; } /* Remove all signals in src from dst. */ void VG_(sigdelset_from_set)( vki_sigset_t* dst, vki_sigset_t* src ) { Int i; vg_assert(dst != NULL && src != NULL); for (i = 0; i < _VKI_NSIG_WORDS; i++) dst->sig[i] &= ~(src->sig[i]); } /* The functions sigaction, sigprocmask, sigpending and sigsuspend return 0 on success and -1 on error. */ Int VG_(sigprocmask)( Int how, const vki_sigset_t* set, vki_sigset_t* oldset) { SysRes res = VG_(do_syscall4)(__NR_rt_sigprocmask, how, (UWord)set, (UWord)oldset, _VKI_NSIG_WORDS * sizeof(UWord)); return res.isError ? -1 : 0; } Int VG_(sigaction) ( Int signum, const struct vki_sigaction* act, struct vki_sigaction* oldact) { SysRes res = VG_(do_syscall4)(__NR_rt_sigaction, signum, (UWord)act, (UWord)oldact, _VKI_NSIG_WORDS * sizeof(UWord)); return res.isError ? -1 : 0; } Int VG_(kill)( Int pid, Int signo ) { SysRes res = VG_(do_syscall2)(__NR_kill, pid, signo); return res.isError ? -1 : 0; } Int VG_(tkill)( ThreadId tid, Int signo ) { SysRes res = VG_(mk_SysRes_Error)(VKI_ENOSYS); res = VG_(do_syscall2)(__NR_tkill, tid, signo); if (res.isError && res.err == VKI_ENOSYS) res = VG_(do_syscall2)(__NR_kill, tid, signo); return res.isError ? -1 : 0; } /* A cut-down version of POSIX sigtimedwait: poll for pending signals mentioned in the sigset_t, and if any are present, select one arbitrarily, return its number (which must be > 0), and put auxiliary info about it in the siginfo_t, and make it not-pending-any-more. If none are pending, return zero. The _zero refers to the fact that there is zero timeout, so if no signals are pending it returns immediately. Perhaps a better name would be 'sigpoll'. Returns -1 on error, 0 if no signals pending, and n > 0 if signal n was selected. The Linux implementation is trivial: do the corresponding syscall. The AIX implementation is horrible and probably broken in a dozen obscure ways. I suspect it's only thread-safe because V forces single-threadedness. */ #if defined(VGO_linux) Int VG_(sigtimedwait_zero)( const vki_sigset_t *set, vki_siginfo_t *info ) { static const struct vki_timespec zero = { 0, 0 }; SysRes res = VG_(do_syscall4)(__NR_rt_sigtimedwait, (UWord)set, (UWord)info, (UWord)&zero, sizeof(*set)); return res.isError ? -1 : res.res; } #elif defined(VGO_aix5) /* The general idea is: - use sigpending to find out which signals are pending - choose one - temporarily set its handler to sigtimedwait_zero_handler - use sigsuspend atomically unblock it and wait for the signal. Upon return, sigsuspend restores the signal mask to what it was to start with. - Restore the handler for the signal to whatever it was before. */ /* A signal handler which does nothing (it doesn't need to). It does however check that it's not handing a sync signal for which returning is meaningless. */ static void sigtimedwait_zero_handler ( Int sig ) { vg_assert(sig != VKI_SIGILL); vg_assert(sig != VKI_SIGSEGV); vg_assert(sig != VKI_SIGBUS); vg_assert(sig != VKI_SIGTRAP); /* do nothing */ } Int VG_(sigtimedwait_zero)( const vki_sigset_t *set, vki_siginfo_t *info ) { Int i, ir; SysRes sr; vki_sigset_t pending, blocked, allbutone; struct vki_sigaction sa, saved_sa; /* Find out what's pending: AIX _sigpending */ sr = VG_(do_syscall1)(__NR__sigpending, (UWord)&pending); vg_assert(!sr.isError); /* don't try for signals not in 'set' */ /* pending = pending `intersect` set */ for (i = 0; i < _VKI_NSIG_WORDS; i++) pending.sig[i] &= set->sig[i]; /* don't try for signals not blocked at the moment */ ir = VG_(sigprocmask)(VKI_SIG_SETMASK, NULL, &blocked); vg_assert(ir == 0); /* pending = pending `intersect` blocked */ for (i = 0; i < _VKI_NSIG_WORDS; i++) pending.sig[i] &= blocked.sig[i]; /* decide which signal we're going to snarf */ for (i = 1; i < _VKI_NSIG; i++) if (VG_(sigismember)(&pending,i)) break; if (i == _VKI_NSIG) return 0; /* fetch signal i. pre: i is blocked and pending pre: we are the only thread running */ /* Set up alternative signal handler */ VG_(sigfillset)(&allbutone); VG_(sigdelset)(&allbutone, i); sa.sa_mask = allbutone; sa.ksa_handler = &sigtimedwait_zero_handler; sa.sa_flags = 0; ir = VG_(sigaction)(i, &sa, &saved_sa); vg_assert(ir == 0); /* Switch signal masks and wait for the signal. This should happen immediately, since we've already established it is pending and blocked. */ sr = VG_(do_syscall1)(__NR__sigsuspend, (UWord)&allbutone); vg_assert(sr.isError); if (0) VG_(debugLog)(0, "libcsignal", "sigtimedwait_zero: sigsuspend got res %ld err %ld\n", sr.res, sr.err); vg_assert(sr.res == (UWord)-1); /* Restore signal's handler to whatever it was before */ ir = VG_(sigaction)(i, &saved_sa, NULL); vg_assert(ir == 0); /* This is bogus - we could get more info from the sighandler. */ VG_(memset)( info, 0, sizeof(*info) ); info->si_signo = i; return i; } #else # error Unknown OS #endif /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/