mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-10 05:37:06 +00:00
1436 lines
46 KiB
C
1436 lines
46 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Implementation of POSIX signals. ---*/
|
|
/*--- vg_signals.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, an x86 protected-mode emulator
|
|
designed for debugging and profiling binaries on x86-Unixes.
|
|
|
|
Copyright (C) 2000-2002 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 LICENSE.
|
|
*/
|
|
|
|
|
|
#include "vg_include.h"
|
|
#include "vg_constants.h"
|
|
#include "vg_unsafe.h"
|
|
#include "valgrind.h" /* for VALGRIND_MAGIC_SEQUENCE */
|
|
|
|
/* Define to give more sanity checking for signals. */
|
|
#define DEBUG_SIGNALS
|
|
|
|
|
|
/* KNOWN BUGS 24 May 02:
|
|
|
|
- A signal is not masked in its own handler. Neither are the
|
|
signals in the signal's blocking mask.
|
|
|
|
- There is only one pending set for the entire process, whereas
|
|
POSIX seems to require each thread have its own pending set.
|
|
This means that a signal can only be pending for one thread at
|
|
a time.
|
|
|
|
- The following causes an infinite loop: start Hugs, Feb 2001
|
|
version, and do Control-C at the prompt. There is an infinite
|
|
series of sigints delivered (to the client); but also seemingly
|
|
to valgrind, which is very strange. I don't know why.
|
|
|
|
Probably a lot more bugs which I haven't discovered yet.
|
|
*/
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Forwards decls.
|
|
------------------------------------------------------------------ */
|
|
|
|
static void vg_oursignalhandler ( Int sigNo );
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
HIGH LEVEL STUFF TO DO WITH SIGNALS: POLICY (MOSTLY)
|
|
------------------------------------------------------------------ */
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Signal state for this process.
|
|
------------------------------------------------------------------ */
|
|
|
|
|
|
/* Base-ment of these arrays[VKI_KNSIG].
|
|
|
|
Valid signal numbers are 1 .. VKI_KNSIG inclusive.
|
|
Rather than subtracting 1 for indexing these arrays, which
|
|
is tedious and error-prone, they are simply dimensioned 1 larger,
|
|
and entry [0] is not used.
|
|
*/
|
|
|
|
|
|
/* -----------------------------------------------------
|
|
Static client signal state (SCSS). This is the state
|
|
that the client thinks it has the kernel in.
|
|
SCSS records verbatim the client's settings. These
|
|
are mashed around only when SKSS is calculated from it.
|
|
-------------------------------------------------- */
|
|
|
|
typedef
|
|
struct {
|
|
void* scss_handler; /* VKI_SIG_DFL or VKI_SIG_IGN or ptr to
|
|
client's handler */
|
|
UInt scss_flags;
|
|
vki_ksigset_t scss_mask;
|
|
void* scss_restorer; /* god knows; we ignore it. */
|
|
}
|
|
SCSS_Per_Signal;
|
|
|
|
typedef
|
|
struct {
|
|
/* per-signal info */
|
|
SCSS_Per_Signal scss_per_sig[1+VKI_KNSIG];
|
|
|
|
/* Signal delivery stack, if any. */
|
|
vki_kstack_t altstack;
|
|
|
|
/* Additional elements to SCSS not stored here:
|
|
- for each thread, the thread's blocking mask
|
|
- for each thread in WaitSIG, the set of waited-on sigs
|
|
*/
|
|
}
|
|
SCSS;
|
|
|
|
static SCSS vg_scss;
|
|
|
|
|
|
/* -----------------------------------------------------
|
|
Static kernel signal state (SKSS). This is the state
|
|
that we have the kernel in. It is computed from SCSS.
|
|
-------------------------------------------------- */
|
|
|
|
/* Let's do:
|
|
sigprocmask assigns to all thread masks
|
|
so that at least everything is always consistent
|
|
Flags:
|
|
SA_NOCLDSTOP -- passed to kernel
|
|
SA_ONESHOT or SA_RESETHAND -- required; abort if not set
|
|
SA_RESTART -- we observe this but set our handlers always to restart
|
|
SA_NOMASK or SA_NODEFER -- required to not be set; abort if set
|
|
SA_ONSTACK -- currently not supported; abort if set.
|
|
*/
|
|
|
|
|
|
typedef
|
|
struct {
|
|
void* skss_handler; /* VKI_SIG_DFL or VKI_SIG_IGN
|
|
or ptr to our handler */
|
|
UInt skss_flags;
|
|
/* There is no skss_mask, since we know that we will always ask
|
|
for all signals to be blocked in our one-and-only
|
|
sighandler. */
|
|
/* Also there is no skss_restorer. */
|
|
}
|
|
SKSS_Per_Signal;
|
|
|
|
typedef
|
|
struct {
|
|
SKSS_Per_Signal skss_per_sig[1+VKI_KNSIG];
|
|
vki_ksigset_t skss_sigmask; /* process' blocked signal mask */
|
|
}
|
|
SKSS;
|
|
|
|
static SKSS vg_skss;
|
|
|
|
|
|
/* -----------------------------------------------------
|
|
Dynamic client signal state (DCSS). This holds transient
|
|
information about state of client signals.
|
|
-------------------------------------------------- */
|
|
|
|
typedef
|
|
struct {
|
|
/* True iff a signal has been received but not yet passed to
|
|
client. */
|
|
Bool dcss_sigpending[1+VKI_KNSIG];
|
|
/* If sigpending[] is True, has meaning:
|
|
VG_INVALID_THREADID -- to be passed to any suitable thread
|
|
other -- to be passed only to the specified thread. */
|
|
ThreadId dcss_destthread[1+VKI_KNSIG];
|
|
}
|
|
DCSS;
|
|
|
|
static DCSS vg_dcss;
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Compute the SKSS required by the current SCSS.
|
|
------------------------------------------------------------------ */
|
|
|
|
static
|
|
void pp_SKSS ( void )
|
|
{
|
|
Int sig;
|
|
VG_(printf)("\n\nSKSS:\n");
|
|
for (sig = 1; sig <= VKI_KNSIG; sig++) {
|
|
VG_(printf)("sig %d: handler 0x%x, flags 0x%x\n", sig,
|
|
vg_skss.skss_per_sig[sig].skss_handler,
|
|
vg_skss.skss_per_sig[sig].skss_flags );
|
|
|
|
}
|
|
VG_(printf)("Global sigmask (63 .. 0) = 0x%x 0x%x\n",
|
|
vg_skss.skss_sigmask.ws[1],
|
|
vg_skss.skss_sigmask.ws[0] );
|
|
}
|
|
|
|
static __inline__
|
|
Bool is_WaitSIGd_by_any_thread ( Int sig )
|
|
{
|
|
ThreadId tid;
|
|
for (tid = 1; tid < VG_N_THREADS; tid++) {
|
|
if (VG_(threads)[tid].status != VgTs_WaitSIG)
|
|
continue;
|
|
if (VG_(ksigismember)( &VG_(threads)[tid].sigs_waited_for, sig ))
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static __inline__
|
|
Bool is_blocked_by_all_threads ( Int sig )
|
|
{
|
|
ThreadId tid;
|
|
for (tid = 1; tid < VG_N_THREADS; tid++) {
|
|
if (VG_(threads)[tid].status == VgTs_Empty)
|
|
continue;
|
|
if (! VG_(ksigismember)( &VG_(threads)[tid].sig_mask, sig ))
|
|
return False;
|
|
}
|
|
return True;
|
|
}
|
|
|
|
|
|
/* This is the core, clever bit. Computation is as follows:
|
|
|
|
For each signal
|
|
handler = if client has a handler, then our handler
|
|
else if is WaitSIG'd by any thread, then our handler
|
|
else if client is DFL, then DFL
|
|
else (client must be IGN) IGN
|
|
|
|
blocked = if is blocked by all threads and not WaitSIG'd by
|
|
any thread
|
|
then BLOCKED
|
|
else UNBLOCKED
|
|
*/
|
|
static
|
|
void calculate_SKSS_from_SCSS ( SKSS* dst )
|
|
{
|
|
Int sig;
|
|
void* skss_handler;
|
|
void* scss_handler;
|
|
Bool iz_WaitSIGd_by_any_thread;
|
|
Bool iz_blocked_by_all_threads;
|
|
Bool skss_blocked;
|
|
UInt scss_flags;
|
|
UInt skss_flags;
|
|
|
|
VG_(ksigemptyset)( &dst->skss_sigmask );
|
|
|
|
for (sig = 1; sig <= VKI_KNSIG; sig++) {
|
|
|
|
/* Calculate kernel handler and blockedness for sig, as per rules
|
|
in above comment. */
|
|
|
|
iz_WaitSIGd_by_any_thread = is_WaitSIGd_by_any_thread(sig);
|
|
iz_blocked_by_all_threads = is_blocked_by_all_threads(sig);
|
|
|
|
scss_handler = vg_scss.scss_per_sig[sig].scss_handler;
|
|
scss_flags = vg_scss.scss_per_sig[sig].scss_flags;
|
|
|
|
/* Restorer */
|
|
/*
|
|
Doesn't seem like we can spin this one.
|
|
if (vg_scss.scss_per_sig[sig].scss_restorer != NULL)
|
|
VG_(unimplemented)
|
|
("sigactions with non-NULL .sa_restorer field");
|
|
*/
|
|
|
|
/* Handler */
|
|
|
|
if (scss_handler != VKI_SIG_DFL && scss_handler != VKI_SIG_IGN) {
|
|
skss_handler = &vg_oursignalhandler;
|
|
} else
|
|
if (iz_WaitSIGd_by_any_thread) {
|
|
skss_handler = &vg_oursignalhandler;
|
|
} else
|
|
if (scss_handler == VKI_SIG_DFL) {
|
|
skss_handler = VKI_SIG_DFL;
|
|
}
|
|
else {
|
|
vg_assert(scss_handler == VKI_SIG_IGN);
|
|
skss_handler = VKI_SIG_IGN;
|
|
}
|
|
|
|
/* Blockfulness */
|
|
|
|
skss_blocked
|
|
= iz_blocked_by_all_threads && !iz_WaitSIGd_by_any_thread;
|
|
|
|
/* Flags */
|
|
|
|
skss_flags = 0;
|
|
/* SA_NOCLDSTOP: pass to kernel */
|
|
if (scss_flags & VKI_SA_NOCLDSTOP)
|
|
skss_flags |= VKI_SA_NOCLDSTOP;
|
|
/* SA_ONESHOT: ignore client setting */
|
|
/*
|
|
if (!(scss_flags & VKI_SA_ONESHOT))
|
|
VG_(unimplemented)
|
|
("sigactions without SA_ONESHOT");
|
|
vg_assert(scss_flags & VKI_SA_ONESHOT);
|
|
skss_flags |= VKI_SA_ONESHOT;
|
|
*/
|
|
/* SA_RESTART: ignore client setting and set for us */
|
|
skss_flags |= VKI_SA_RESTART;
|
|
/* SA_NOMASK: not allowed */
|
|
/*
|
|
.. well, ignore it.
|
|
if (scss_flags & VKI_SA_NOMASK)
|
|
VG_(unimplemented)
|
|
("sigactions with SA_NOMASK");
|
|
vg_assert(!(scss_flags & VKI_SA_NOMASK));
|
|
*/
|
|
/* SA_ONSTACK: client setting is irrelevant here */
|
|
/*
|
|
if (scss_flags & VKI_SA_ONSTACK)
|
|
VG_(unimplemented)
|
|
("signals on an alternative stack (SA_ONSTACK)");
|
|
vg_assert(!(scss_flags & VKI_SA_ONSTACK));
|
|
*/
|
|
/* ... but WE ask for on-stack ourselves ... */
|
|
skss_flags |= VKI_SA_ONSTACK;
|
|
|
|
/* Create SKSS entry for this signal. */
|
|
|
|
if (skss_blocked
|
|
&& sig != VKI_SIGKILL && sig != VKI_SIGSTOP)
|
|
VG_(ksigaddset)( &dst->skss_sigmask, sig );
|
|
|
|
if (sig != VKI_SIGKILL && sig != VKI_SIGSTOP)
|
|
dst->skss_per_sig[sig].skss_handler = skss_handler;
|
|
else
|
|
dst->skss_per_sig[sig].skss_handler = VKI_SIG_DFL;
|
|
|
|
dst->skss_per_sig[sig].skss_flags = skss_flags;
|
|
}
|
|
|
|
/* Sanity checks. */
|
|
vg_assert(dst->skss_per_sig[VKI_SIGKILL].skss_handler
|
|
== VKI_SIG_DFL);
|
|
vg_assert(dst->skss_per_sig[VKI_SIGSTOP].skss_handler
|
|
== VKI_SIG_DFL);
|
|
vg_assert(!VG_(ksigismember)( &dst->skss_sigmask, VKI_SIGKILL ));
|
|
vg_assert(!VG_(ksigismember)( &dst->skss_sigmask, VKI_SIGSTOP ));
|
|
|
|
if (0)
|
|
pp_SKSS();
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
After a possible SCSS change, update SKSS and the kernel itself.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* IMPORTANT NOTE: to avoid race conditions, we must always enter here
|
|
with ALL KERNEL SIGNALS BLOCKED !
|
|
*/
|
|
void VG_(handle_SCSS_change) ( Bool force_update )
|
|
{
|
|
Int res, sig;
|
|
SKSS skss_old;
|
|
vki_ksigaction ksa, ksa_old;
|
|
|
|
# ifdef DEBUG_SIGNALS
|
|
vki_ksigset_t test_sigmask;
|
|
res = VG_(ksigprocmask)( VKI_SIG_SETMASK /*irrelevant*/,
|
|
NULL, &test_sigmask );
|
|
vg_assert(res == 0);
|
|
/* The kernel never says that SIGKILL or SIGSTOP are masked. It is
|
|
correct! So we fake it here for the purposes only of
|
|
assertion. */
|
|
VG_(ksigaddset)( &test_sigmask, VKI_SIGKILL );
|
|
VG_(ksigaddset)( &test_sigmask, VKI_SIGSTOP );
|
|
vg_assert(VG_(kisfullsigset)( &test_sigmask ));
|
|
# endif
|
|
|
|
/* Remember old SKSS and calculate new one. */
|
|
skss_old = vg_skss;
|
|
calculate_SKSS_from_SCSS ( &vg_skss );
|
|
|
|
/* Compare the new SKSS entries vs the old ones, and update kernel
|
|
where they differ. */
|
|
for (sig = 1; sig <= VKI_KNSIG; sig++) {
|
|
|
|
/* Trying to do anything with SIGKILL is pointless; just ignore
|
|
it. */
|
|
if (sig == VKI_SIGKILL || sig == VKI_SIGSTOP)
|
|
continue;
|
|
|
|
/* Aside: take the opportunity to clean up DCSS: forget about any
|
|
pending signals directed at dead threads. */
|
|
if (vg_dcss.dcss_sigpending[sig]
|
|
&& vg_dcss.dcss_destthread[sig] != VG_INVALID_THREADID) {
|
|
ThreadId tid = vg_dcss.dcss_destthread[sig];
|
|
vg_assert(VG_(is_valid_or_empty_tid)(tid));
|
|
if (VG_(threads)[tid].status == VgTs_Empty) {
|
|
vg_dcss.dcss_sigpending[sig] = False;
|
|
vg_dcss.dcss_destthread[sig] = VG_INVALID_THREADID;
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"discarding pending signal %d due to thread %d exiting",
|
|
sig, tid );
|
|
}
|
|
}
|
|
|
|
/* End of the Aside. Now the Main Business. */
|
|
|
|
if (!force_update) {
|
|
if ((skss_old.skss_per_sig[sig].skss_handler
|
|
== vg_skss.skss_per_sig[sig].skss_handler)
|
|
&& (skss_old.skss_per_sig[sig].skss_flags
|
|
== vg_skss.skss_per_sig[sig].skss_flags))
|
|
/* no difference */
|
|
continue;
|
|
}
|
|
|
|
ksa.ksa_handler = vg_skss.skss_per_sig[sig].skss_handler;
|
|
ksa.ksa_flags = vg_skss.skss_per_sig[sig].skss_flags;
|
|
vg_assert(ksa.ksa_flags & VKI_SA_ONSTACK);
|
|
VG_(ksigfillset)( &ksa.ksa_mask );
|
|
VG_(ksigdelset)( &ksa.ksa_mask, VKI_SIGKILL );
|
|
VG_(ksigdelset)( &ksa.ksa_mask, VKI_SIGSTOP );
|
|
ksa.ksa_restorer = NULL;
|
|
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"setting ksig %d to: hdlr 0x%x, flags 0x%x, "
|
|
"mask(63..0) 0x%x 0x%x",
|
|
sig, ksa.ksa_handler,
|
|
ksa.ksa_flags,
|
|
ksa.ksa_mask.ws[1],
|
|
ksa.ksa_mask.ws[0]
|
|
);
|
|
|
|
res = VG_(ksigaction)( sig, &ksa, &ksa_old );
|
|
vg_assert(res == 0);
|
|
|
|
/* Since we got the old sigaction more or less for free, might
|
|
as well extract the maximum sanity-check value from it. */
|
|
if (!force_update) {
|
|
vg_assert(ksa_old.ksa_handler
|
|
== skss_old.skss_per_sig[sig].skss_handler);
|
|
vg_assert(ksa_old.ksa_flags
|
|
== skss_old.skss_per_sig[sig].skss_flags);
|
|
vg_assert(ksa_old.ksa_restorer
|
|
== NULL);
|
|
VG_(ksigaddset)( &ksa_old.ksa_mask, VKI_SIGKILL );
|
|
VG_(ksigaddset)( &ksa_old.ksa_mask, VKI_SIGSTOP );
|
|
vg_assert(VG_(kisfullsigset)( &ksa_old.ksa_mask ));
|
|
}
|
|
}
|
|
|
|
/* Just set the new sigmask, even if it's no different from the
|
|
old, since we have to do this anyway, to unblock the host
|
|
signals. */
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"setting kmask(63..0) to 0x%x 0x%x",
|
|
vg_skss.skss_sigmask.ws[1],
|
|
vg_skss.skss_sigmask.ws[0]
|
|
);
|
|
|
|
VG_(restore_all_host_signals)( &vg_skss.skss_sigmask );
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Update/query SCSS in accordance with client requests.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* Logic for this alt-stack stuff copied directly from do_sigaltstack
|
|
in kernel/signal.[ch] */
|
|
|
|
/* True if we are on the alternate signal stack. */
|
|
static Int on_sig_stack ( Addr m_esp )
|
|
{
|
|
return (m_esp - (Addr)vg_scss.altstack.ss_sp
|
|
< vg_scss.altstack.ss_size);
|
|
}
|
|
|
|
static Int sas_ss_flags ( Addr m_esp )
|
|
{
|
|
return (vg_scss.altstack.ss_size == 0
|
|
? VKI_SS_DISABLE
|
|
: on_sig_stack(m_esp) ? VKI_SS_ONSTACK : 0);
|
|
}
|
|
|
|
|
|
void VG_(do__NR_sigaltstack) ( ThreadId tid )
|
|
{
|
|
vki_kstack_t* ss;
|
|
vki_kstack_t* oss;
|
|
Addr m_esp;
|
|
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
ss = (vki_kstack_t*)(VG_(threads)[tid].m_ebx);
|
|
oss = (vki_kstack_t*)(VG_(threads)[tid].m_ecx);
|
|
m_esp = VG_(threads)[tid].m_esp;
|
|
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugExtraMsg,
|
|
"__NR_sigaltstack: tid %d, "
|
|
"ss 0x%x, oss 0x%x (current %%esp %p)",
|
|
tid, (UInt)ss, (UInt)oss, (UInt)m_esp );
|
|
|
|
if (oss != NULL) {
|
|
oss->ss_sp = vg_scss.altstack.ss_sp;
|
|
oss->ss_size = vg_scss.altstack.ss_size;
|
|
oss->ss_flags = sas_ss_flags(m_esp);
|
|
}
|
|
|
|
if (ss != NULL) {
|
|
if (on_sig_stack(VG_(threads)[tid].m_esp)) {
|
|
SET_EAX(tid, -VKI_EPERM);
|
|
return;
|
|
}
|
|
if (ss->ss_flags != VKI_SS_DISABLE
|
|
&& ss->ss_flags != VKI_SS_ONSTACK
|
|
&& ss->ss_flags != 0) {
|
|
SET_EAX(tid, -VKI_EINVAL);
|
|
return;
|
|
}
|
|
if (ss->ss_flags == VKI_SS_DISABLE) {
|
|
vg_scss.altstack.ss_size = 0;
|
|
vg_scss.altstack.ss_sp = NULL;
|
|
} else {
|
|
if (ss->ss_size < VKI_MINSIGSTKSZ) {
|
|
SET_EAX(tid, -VKI_ENOMEM);
|
|
return;
|
|
}
|
|
}
|
|
vg_scss.altstack.ss_sp = ss->ss_sp;
|
|
vg_scss.altstack.ss_size = ss->ss_size;
|
|
}
|
|
SET_EAX(tid, 0);
|
|
}
|
|
|
|
|
|
void VG_(do__NR_sigaction) ( ThreadId tid )
|
|
{
|
|
Int signo;
|
|
vki_ksigaction* new_act;
|
|
vki_ksigaction* old_act;
|
|
vki_ksigset_t irrelevant_sigmask;
|
|
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
signo = VG_(threads)[tid].m_ebx; /* int sigNo */
|
|
new_act = (vki_ksigaction*)(VG_(threads)[tid].m_ecx);
|
|
old_act = (vki_ksigaction*)(VG_(threads)[tid].m_edx);
|
|
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugExtraMsg,
|
|
"__NR_sigaction: tid %d, sigNo %d, "
|
|
"new 0x%x, old 0x%x, new flags 0x%x",
|
|
tid, signo, (UInt)new_act, (UInt)old_act,
|
|
(UInt)(new_act ? new_act->ksa_flags : 0) );
|
|
|
|
/* Rule out various error conditions. The aim is to ensure that if
|
|
when the call is passed to the kernel it will definitely
|
|
succeed. */
|
|
|
|
/* Reject out-of-range signal numbers. */
|
|
if (signo < 1 || signo > VKI_KNSIG) goto bad_signo;
|
|
|
|
/* Reject attempts to set a handler (or set ignore) for SIGKILL. */
|
|
if ( (signo == VKI_SIGKILL || signo == VKI_SIGSTOP)
|
|
&& new_act
|
|
&& new_act->ksa_handler != VKI_SIG_DFL)
|
|
goto bad_sigkill_or_sigstop;
|
|
|
|
/* If the client supplied non-NULL old_act, copy the relevant SCSS
|
|
entry into it. */
|
|
if (old_act) {
|
|
old_act->ksa_handler = vg_scss.scss_per_sig[signo].scss_handler;
|
|
old_act->ksa_flags = vg_scss.scss_per_sig[signo].scss_flags;
|
|
old_act->ksa_mask = vg_scss.scss_per_sig[signo].scss_mask;
|
|
old_act->ksa_restorer = vg_scss.scss_per_sig[signo].scss_restorer;
|
|
}
|
|
|
|
/* And now copy new SCSS entry from new_act. */
|
|
if (new_act) {
|
|
vg_scss.scss_per_sig[signo].scss_handler = new_act->ksa_handler;
|
|
vg_scss.scss_per_sig[signo].scss_flags = new_act->ksa_flags;
|
|
vg_scss.scss_per_sig[signo].scss_mask = new_act->ksa_mask;
|
|
vg_scss.scss_per_sig[signo].scss_restorer = new_act->ksa_restorer;
|
|
}
|
|
|
|
/* All happy bunnies ... */
|
|
if (new_act) {
|
|
VG_(block_all_host_signals)( &irrelevant_sigmask );
|
|
VG_(handle_SCSS_change)( False /* lazy update */ );
|
|
}
|
|
SET_EAX(tid, 0);
|
|
return;
|
|
|
|
bad_signo:
|
|
VG_(message)(Vg_UserMsg,
|
|
"Warning: bad signal number %d in __NR_sigaction.",
|
|
signo);
|
|
SET_EAX(tid, -VKI_EINVAL);
|
|
return;
|
|
|
|
bad_sigkill_or_sigstop:
|
|
VG_(message)(Vg_UserMsg,
|
|
"Warning: attempt to set %s handler in __NR_sigaction.",
|
|
signo == VKI_SIGKILL ? "SIGKILL" : "SIGSTOP" );
|
|
|
|
SET_EAX(tid, -VKI_EINVAL);
|
|
return;
|
|
}
|
|
|
|
|
|
static
|
|
void do_sigprocmask_bitops ( Int vki_how,
|
|
vki_ksigset_t* orig_set,
|
|
vki_ksigset_t* modifier )
|
|
{
|
|
switch (vki_how) {
|
|
case VKI_SIG_BLOCK:
|
|
VG_(ksigaddset_from_set)( orig_set, modifier );
|
|
break;
|
|
case VKI_SIG_UNBLOCK:
|
|
VG_(ksigdelset_from_set)( orig_set, modifier );
|
|
break;
|
|
case VKI_SIG_SETMASK:
|
|
*orig_set = *modifier;
|
|
break;
|
|
default:
|
|
VG_(panic)("do_sigprocmask_bitops");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Handle blocking mask set/get uniformly for threads and process as a
|
|
whole. If tid==VG_INVALID_THREADID, this is really
|
|
__NR_sigprocmask, in which case we set the masks for all threads to
|
|
the "set" and return in "oldset" that from the root thread (1).
|
|
Otherwise, tid will denote a valid thread, in which case we just
|
|
set/get its mask.
|
|
|
|
Note that the thread signal masks are an implicit part of SCSS,
|
|
which is why this routine is allowed to mess with them.
|
|
*/
|
|
static
|
|
void do_setmask ( ThreadId tid,
|
|
Int how,
|
|
vki_ksigset_t* newset,
|
|
vki_ksigset_t* oldset )
|
|
{
|
|
vki_ksigset_t irrelevant_sigmask;
|
|
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugExtraMsg,
|
|
"do_setmask: tid = %d (%d means ALL), how = %d (%s), set = %p",
|
|
tid,
|
|
VG_INVALID_THREADID,
|
|
how,
|
|
how==VKI_SIG_BLOCK ? "SIG_BLOCK" : (
|
|
how==VKI_SIG_UNBLOCK ? "SIG_UNBLOCK" : (
|
|
how==VKI_SIG_SETMASK ? "SIG_SETMASK" : "???")),
|
|
newset
|
|
);
|
|
|
|
if (tid == VG_INVALID_THREADID) {
|
|
/* Behave as if __NR_sigprocmask. */
|
|
if (oldset) {
|
|
/* A bit fragile. Should do better here really. */
|
|
vg_assert(VG_(threads)[1].status != VgTs_Empty);
|
|
*oldset = VG_(threads)[1].sig_mask;
|
|
}
|
|
if (newset) {
|
|
ThreadId tidd;
|
|
for (tidd = 1; tidd < VG_N_THREADS; tidd++) {
|
|
if (VG_(threads)[tidd].status == VgTs_Empty)
|
|
continue;
|
|
do_sigprocmask_bitops (
|
|
how, &VG_(threads)[tidd].sig_mask, newset );
|
|
}
|
|
}
|
|
} else {
|
|
/* Just do this thread. */
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
if (oldset)
|
|
*oldset = VG_(threads)[tid].sig_mask;
|
|
if (newset)
|
|
do_sigprocmask_bitops (
|
|
how, &VG_(threads)[tid].sig_mask, newset );
|
|
}
|
|
|
|
if (newset) {
|
|
VG_(block_all_host_signals)( &irrelevant_sigmask );
|
|
VG_(handle_SCSS_change)( False /* lazy update */ );
|
|
}
|
|
}
|
|
|
|
|
|
void VG_(do__NR_sigprocmask) ( ThreadId tid,
|
|
Int how,
|
|
vki_ksigset_t* set,
|
|
vki_ksigset_t* oldset )
|
|
{
|
|
if (how == VKI_SIG_BLOCK || how == VKI_SIG_UNBLOCK
|
|
|| how == VKI_SIG_SETMASK) {
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
do_setmask ( VG_INVALID_THREADID, how, set, oldset );
|
|
/* Syscall returns 0 (success) to its thread. */
|
|
SET_EAX(tid, 0);
|
|
} else {
|
|
VG_(message)(Vg_DebugMsg,
|
|
"sigprocmask: unknown `how' field %d", how);
|
|
SET_EAX(tid, -VKI_EINVAL);
|
|
}
|
|
}
|
|
|
|
|
|
void VG_(do_pthread_sigmask_SCSS_upd) ( ThreadId tid,
|
|
Int how,
|
|
vki_ksigset_t* set,
|
|
vki_ksigset_t* oldset )
|
|
{
|
|
/* Assume that how has been validated by caller. */
|
|
vg_assert(how == VKI_SIG_BLOCK || how == VKI_SIG_UNBLOCK
|
|
|| how == VKI_SIG_SETMASK);
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
do_setmask ( tid, how, set, oldset );
|
|
/* The request return code is set in do_pthread_sigmask */
|
|
}
|
|
|
|
|
|
void VG_(send_signal_to_thread) ( ThreadId thread, Int sig )
|
|
{
|
|
Int res;
|
|
vg_assert(VG_(is_valid_tid)(thread));
|
|
vg_assert(sig >= 1 && sig <= VKI_KNSIG);
|
|
|
|
switch ((UInt)(vg_scss.scss_per_sig[sig].scss_handler)) {
|
|
|
|
case ((UInt)VKI_SIG_IGN):
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"send_signal %d to_thread %d: IGN, ignored", sig, thread );
|
|
break;
|
|
|
|
case ((UInt)VKI_SIG_DFL):
|
|
/* This is the tricky case. Since we don't handle default
|
|
actions, the simple thing is to send someone round to the
|
|
front door and signal there. Then the kernel will do
|
|
whatever it does with the default action. */
|
|
res = VG_(kill)( VG_(getpid)(), sig );
|
|
vg_assert(res == 0);
|
|
break;
|
|
|
|
default:
|
|
if (!vg_dcss.dcss_sigpending[sig]) {
|
|
vg_dcss.dcss_sigpending[sig] = True;
|
|
vg_dcss.dcss_destthread[sig] = thread;
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"send_signal %d to_thread %d: now pending", sig, thread );
|
|
} else {
|
|
if (vg_dcss.dcss_destthread[sig] == thread) {
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"send_signal %d to_thread %d: already pending ... "
|
|
"discarded", sig, thread );
|
|
} else {
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"send_signal %d to_thread %d: was pending for %d, "
|
|
"now pending for %d",
|
|
sig, thread, vg_dcss.dcss_destthread[sig], thread );
|
|
vg_dcss.dcss_destthread[sig] = thread;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
LOW LEVEL STUFF TO DO WITH SIGNALS: IMPLEMENTATION
|
|
------------------------------------------------------------------ */
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Handy utilities to block/restore all host signals.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* Block all host signals, dumping the old mask in *saved_mask. */
|
|
void VG_(block_all_host_signals) ( /* OUT */ vki_ksigset_t* saved_mask )
|
|
{
|
|
Int ret;
|
|
vki_ksigset_t block_procmask;
|
|
VG_(ksigfillset)(&block_procmask);
|
|
ret = VG_(ksigprocmask)
|
|
(VKI_SIG_SETMASK, &block_procmask, saved_mask);
|
|
vg_assert(ret == 0);
|
|
}
|
|
|
|
/* Restore the blocking mask using the supplied saved one. */
|
|
void VG_(restore_all_host_signals) ( /* IN */ vki_ksigset_t* saved_mask )
|
|
{
|
|
Int ret;
|
|
ret = VG_(ksigprocmask)(VKI_SIG_SETMASK, saved_mask, NULL);
|
|
vg_assert(ret == 0);
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
The signal simulation proper. A simplified version of what the
|
|
Linux kernel does.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* A structure in which to save the application's registers
|
|
during the execution of signal handlers. */
|
|
|
|
typedef
|
|
struct {
|
|
/* These are parameters to the signal handler. */
|
|
UInt retaddr; /* Sig handler's (bogus) return address */
|
|
Int sigNo; /* The arg to the sig handler. */
|
|
Addr psigInfo; /* ptr to siginfo_t; NULL for now. */
|
|
Addr puContext; /* ptr to ucontext; NULL for now. */
|
|
/* Sanity check word. */
|
|
UInt magicPI;
|
|
/* Saved processor state. */
|
|
UInt fpustate[VG_SIZE_OF_FPUSTATE_W];
|
|
UInt eax;
|
|
UInt ecx;
|
|
UInt edx;
|
|
UInt ebx;
|
|
UInt ebp;
|
|
UInt esp;
|
|
UInt esi;
|
|
UInt edi;
|
|
Addr eip;
|
|
UInt eflags;
|
|
/* Scheduler-private stuff: what was the thread's status prior to
|
|
delivering this signal? */
|
|
ThreadStatus status;
|
|
/* Sanity check word. Is the highest-addressed word; do not
|
|
move!*/
|
|
UInt magicE;
|
|
}
|
|
VgSigFrame;
|
|
|
|
|
|
|
|
/* Set up a stack frame (VgSigContext) for the client's signal
|
|
handler. This includes the signal number and a bogus return
|
|
address. */
|
|
static
|
|
void vg_push_signal_frame ( ThreadId tid, int sigNo )
|
|
{
|
|
Int i;
|
|
Addr esp, esp_top_of_frame;
|
|
VgSigFrame* frame;
|
|
ThreadState* tst;
|
|
|
|
vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
tst = & VG_(threads)[tid];
|
|
|
|
if (/* this signal asked to run on an alt stack */
|
|
(vg_scss.scss_per_sig[sigNo].scss_flags & VKI_SA_ONSTACK)
|
|
&& /* there is a defined and enabled alt stack, which we're not
|
|
already using. Logic from get_sigframe in
|
|
arch/i386/kernel/signal.c. */
|
|
sas_ss_flags(tst->m_esp) == 0
|
|
) {
|
|
esp_top_of_frame
|
|
= (Addr)(vg_scss.altstack.ss_sp) + vg_scss.altstack.ss_size;
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"delivering signal %d to thread %d: on ALT STACK",
|
|
sigNo, tid );
|
|
} else {
|
|
esp_top_of_frame = tst->m_esp;
|
|
}
|
|
|
|
esp = esp_top_of_frame;
|
|
esp -= sizeof(VgSigFrame);
|
|
frame = (VgSigFrame*)esp;
|
|
/* Assert that the frame is placed correctly. */
|
|
vg_assert( (sizeof(VgSigFrame) & 0x3) == 0 );
|
|
vg_assert( ((Char*)(&frame->magicE)) + sizeof(UInt)
|
|
== ((Char*)(esp_top_of_frame)) );
|
|
|
|
frame->retaddr = (UInt)(&VG_(signalreturn_bogusRA));
|
|
frame->sigNo = sigNo;
|
|
frame->psigInfo = (Addr)NULL;
|
|
frame->puContext = (Addr)NULL;
|
|
frame->magicPI = 0x31415927;
|
|
|
|
for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
|
|
frame->fpustate[i] = tst->m_fpu[i];
|
|
|
|
frame->eax = tst->m_eax;
|
|
frame->ecx = tst->m_ecx;
|
|
frame->edx = tst->m_edx;
|
|
frame->ebx = tst->m_ebx;
|
|
frame->ebp = tst->m_ebp;
|
|
frame->esp = tst->m_esp;
|
|
frame->esi = tst->m_esi;
|
|
frame->edi = tst->m_edi;
|
|
frame->eip = tst->m_eip;
|
|
frame->eflags = tst->m_eflags;
|
|
|
|
frame->status = tst->status;
|
|
|
|
frame->magicE = 0x27182818;
|
|
|
|
/* Set the thread so it will next run the handler. */
|
|
tst->m_esp = esp;
|
|
tst->m_eip = (Addr)vg_scss.scss_per_sig[sigNo].scss_handler;
|
|
/* This thread needs to be marked runnable, but we leave that the
|
|
caller to do. */
|
|
|
|
/* Make sigNo and retaddr fields readable -- at 0(%ESP) and 4(%ESP) */
|
|
if (VG_(clo_instrument)) {
|
|
VGM_(make_readable) ( ((Addr)esp)+0 ,4 );
|
|
VGM_(make_readable) ( ((Addr)esp)+4 ,4 );
|
|
}
|
|
|
|
/*
|
|
VG_(printf)("pushed signal frame; %%ESP now = %p, next %%EBP = %p\n",
|
|
esp, tst->m_eip);
|
|
*/
|
|
}
|
|
|
|
|
|
/* Clear the signal frame created by vg_push_signal_frame, restore the
|
|
simulated machine state, and return the signal number that the
|
|
frame was for. */
|
|
static
|
|
Int vg_pop_signal_frame ( ThreadId tid )
|
|
{
|
|
Addr esp;
|
|
Int sigNo, i;
|
|
VgSigFrame* frame;
|
|
ThreadState* tst;
|
|
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
tst = & VG_(threads)[tid];
|
|
|
|
/* Correctly reestablish the frame base address. */
|
|
esp = tst->m_esp;
|
|
frame = (VgSigFrame*)
|
|
(esp -4 /* because the handler's RET pops the RA */
|
|
+20 /* because signalreturn_bogusRA pushes 5 words */);
|
|
|
|
vg_assert(frame->magicPI == 0x31415927);
|
|
vg_assert(frame->magicE == 0x27182818);
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"vg_pop_signal_frame (thread %d): valid magic", tid);
|
|
|
|
/* restore machine state */
|
|
for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
|
|
tst->m_fpu[i] = frame->fpustate[i];
|
|
|
|
/* Mark the frame structure as nonaccessible. */
|
|
if (VG_(clo_instrument))
|
|
VGM_(make_noaccess)( (Addr)frame, sizeof(VgSigFrame) );
|
|
|
|
/* Restore machine state from the saved context. */
|
|
tst->m_eax = frame->eax;
|
|
tst->m_ecx = frame->ecx;
|
|
tst->m_edx = frame->edx;
|
|
tst->m_ebx = frame->ebx;
|
|
tst->m_ebp = frame->ebp;
|
|
tst->m_esp = frame->esp;
|
|
tst->m_esi = frame->esi;
|
|
tst->m_edi = frame->edi;
|
|
tst->m_eflags = frame->eflags;
|
|
tst->m_eip = frame->eip;
|
|
sigNo = frame->sigNo;
|
|
|
|
/* And restore the thread's status to what it was before the signal
|
|
was delivered. */
|
|
tst->status = frame->status;
|
|
|
|
return sigNo;
|
|
}
|
|
|
|
|
|
/* A handler is returning. Restore the machine state from the stacked
|
|
VgSigContext and continue with whatever was going on before the
|
|
handler ran. Returns the SA_RESTART syscall-restartability-status
|
|
of the delivered signal. */
|
|
|
|
Bool VG_(signal_returns) ( ThreadId tid )
|
|
{
|
|
Int sigNo;
|
|
vki_ksigset_t saved_procmask;
|
|
|
|
/* Block host signals ... */
|
|
VG_(block_all_host_signals)( &saved_procmask );
|
|
|
|
/* Pop the signal frame and restore tid's status to what it was
|
|
before the signal was delivered. */
|
|
sigNo = vg_pop_signal_frame(tid);
|
|
|
|
vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
|
|
|
|
/* Unlock and return. */
|
|
VG_(restore_all_host_signals)( &saved_procmask );
|
|
|
|
/* Scheduler now can resume this thread, or perhaps some other.
|
|
Tell the scheduler whether or not any syscall interrupted by
|
|
this signal should be restarted, if possible, or no. */
|
|
return
|
|
(vg_scss.scss_per_sig[sigNo].scss_flags & VKI_SA_RESTART)
|
|
? True
|
|
: False;
|
|
}
|
|
|
|
|
|
/* Deliver all pending signals, by building stack frames for their
|
|
handlers. Return True if any signals were delivered. */
|
|
Bool VG_(deliver_signals) ( void )
|
|
{
|
|
vki_ksigset_t saved_procmask;
|
|
Int sigNo;
|
|
Bool found, scss_changed;
|
|
ThreadState* tst;
|
|
ThreadId tid;
|
|
|
|
/* A cheap check. We don't need to have exclusive access to the
|
|
pending array, because in the worst case, vg_oursignalhandler
|
|
will add signals, causing us to return, thinking there are no
|
|
signals to deliver, when in fact there are some. A subsequent
|
|
call here will handle the signal(s) we missed. */
|
|
found = False;
|
|
for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++)
|
|
if (vg_dcss.dcss_sigpending[sigNo])
|
|
found = True;
|
|
|
|
if (!found) return False;
|
|
|
|
/* Now we have to do it properly. Get exclusive access by
|
|
blocking all the host's signals. That means vg_oursignalhandler
|
|
can't run whilst we are messing with stuff.
|
|
*/
|
|
scss_changed = False;
|
|
VG_(block_all_host_signals)( &saved_procmask );
|
|
|
|
/* Look for signals to deliver ... */
|
|
for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
|
|
|
|
if (!vg_dcss.dcss_sigpending[sigNo])
|
|
continue;
|
|
|
|
/* sigNo is pending. Try to find a suitable thread to deliver
|
|
it to. */
|
|
/* First off, are any threads in sigwait() for the signal?
|
|
If so just give to one of them and have done. */
|
|
for (tid = 1; tid < VG_N_THREADS; tid++) {
|
|
tst = & VG_(threads)[tid];
|
|
if (tst->status != VgTs_WaitSIG)
|
|
continue;
|
|
if (VG_(ksigismember)(&(tst->sigs_waited_for), sigNo))
|
|
break;
|
|
}
|
|
if (tid < VG_N_THREADS) {
|
|
UInt* sigwait_args;
|
|
tst = & VG_(threads)[tid];
|
|
if (VG_(clo_trace_signals) || VG_(clo_trace_sched))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"releasing thread %d from sigwait() due to signal %d",
|
|
tid, sigNo );
|
|
sigwait_args = (UInt*)(tst->m_eax);
|
|
if (NULL != (UInt*)(sigwait_args[2])) {
|
|
*(Int*)(sigwait_args[2]) = sigNo;
|
|
if (VG_(clo_instrument))
|
|
VGM_(make_readable)( (Addr)(sigwait_args[2]),
|
|
sizeof(UInt));
|
|
}
|
|
SET_EDX(tid, 0);
|
|
tst->status = VgTs_Runnable;
|
|
VG_(ksigemptyset)(&tst->sigs_waited_for);
|
|
scss_changed = True;
|
|
vg_dcss.dcss_sigpending[sigNo] = False;
|
|
vg_dcss.dcss_destthread[sigNo] = VG_INVALID_THREADID;
|
|
/*paranoia*/
|
|
continue; /* for (sigNo = 1; ...) loop */
|
|
}
|
|
|
|
/* Well, nobody appears to be sigwaiting for it. So we really
|
|
are delivering the signal in the usual way. And that the
|
|
client really has a handler for this thread! */
|
|
vg_assert(vg_dcss.dcss_sigpending[sigNo]);
|
|
vg_assert(vg_scss.scss_per_sig[sigNo].scss_handler != VKI_SIG_IGN
|
|
&& vg_scss.scss_per_sig[sigNo].scss_handler != VKI_SIG_DFL);
|
|
|
|
tid = vg_dcss.dcss_destthread[sigNo];
|
|
vg_assert(tid == VG_INVALID_THREADID
|
|
|| VG_(is_valid_tid)(tid));
|
|
|
|
if (tid != VG_INVALID_THREADID) {
|
|
/* directed to a specific thread; ensure it actually still
|
|
exists ... */
|
|
tst = & VG_(threads)[tid];
|
|
if (tst->status == VgTs_Empty) {
|
|
/* dead, for whatever reason; ignore this signal */
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,
|
|
"discarding signal %d for nonexistent thread %d",
|
|
sigNo, tid );
|
|
vg_dcss.dcss_sigpending[sigNo] = False;
|
|
vg_dcss.dcss_destthread[sigNo] = VG_INVALID_THREADID;
|
|
continue; /* for (sigNo = 1; ...) loop */
|
|
}
|
|
} else {
|
|
/* not directed to a specific thread, so search for a
|
|
suitable candidate */
|
|
for (tid = 1; tid < VG_N_THREADS; tid++) {
|
|
tst = & VG_(threads)[tid];
|
|
if (tst->status != VgTs_Empty
|
|
&& !VG_(ksigismember)(&(tst->sig_mask), sigNo))
|
|
break;
|
|
}
|
|
if (tid == VG_N_THREADS)
|
|
/* All threads have this signal blocked, so we can't
|
|
deliver it just now */
|
|
continue; /* for (sigNo = 1; ...) loop */
|
|
}
|
|
|
|
/* Ok, we can deliver signal sigNo to thread tid. */
|
|
|
|
if (VG_(clo_trace_signals))
|
|
VG_(message)(Vg_DebugMsg,"delivering signal %d to thread %d",
|
|
sigNo, tid );
|
|
|
|
/* Create a signal delivery frame, and set the client's %ESP and
|
|
%EIP so that when execution continues, we will enter the
|
|
signal handler with the frame on top of the client's stack,
|
|
as it expects. */
|
|
vg_assert(VG_(is_valid_tid)(tid));
|
|
vg_push_signal_frame ( tid, sigNo );
|
|
VG_(threads)[tid].status = VgTs_Runnable;
|
|
|
|
/* Signify that the signal has been delivered. */
|
|
vg_dcss.dcss_sigpending[sigNo] = False;
|
|
vg_dcss.dcss_destthread[sigNo] = VG_INVALID_THREADID;
|
|
|
|
if (vg_scss.scss_per_sig[sigNo].scss_flags & VKI_SA_ONESHOT) {
|
|
/* Do the ONESHOT thing. */
|
|
vg_scss.scss_per_sig[sigNo].scss_handler = VKI_SIG_DFL;
|
|
scss_changed = True;
|
|
}
|
|
}
|
|
|
|
/* Unlock and return. */
|
|
if (scss_changed) {
|
|
/* handle_SCSS_change computes a new kernel blocking mask and
|
|
applies that. */
|
|
VG_(handle_SCSS_change)( False /* lazy update */ );
|
|
} else {
|
|
/* No SCSS change, so just restore the existing blocking
|
|
mask. */
|
|
VG_(restore_all_host_signals)( &saved_procmask );
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
/* Receive a signal from the host, and either discard it or park it in
|
|
the queue of pending signals. All other signals will be blocked
|
|
when this handler runs. Runs with all host signals blocked, so as
|
|
to have mutual exclusion when adding stuff to the queue. */
|
|
|
|
static
|
|
void vg_oursignalhandler ( Int sigNo )
|
|
{
|
|
ThreadId tid;
|
|
Int dummy_local;
|
|
Bool sane;
|
|
vki_ksigset_t saved_procmask;
|
|
|
|
/*
|
|
if (sigNo == VKI_SIGUSR1) {
|
|
VG_(printf)("YOWZA! SIGUSR1\n\n");
|
|
VG_(clo_trace_pthread_level) = 2;
|
|
VG_(clo_trace_sched) = True;
|
|
VG_(clo_trace_syscalls) = True;
|
|
VG_(clo_trace_signals) = True;
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (VG_(clo_trace_signals)) {
|
|
VG_(start_msg)(Vg_DebugMsg);
|
|
VG_(add_to_msg)("signal %d arrived ... ", sigNo );
|
|
}
|
|
vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
|
|
|
|
/* Sanity check. Ensure we're really running on the signal stack
|
|
we asked for. */
|
|
if ( !(
|
|
((Char*)(&(VG_(sigstack)[0])) <= (Char*)(&dummy_local))
|
|
&&
|
|
((Char*)(&dummy_local) < (Char*)(&(VG_(sigstack)[10000])))
|
|
)
|
|
) {
|
|
VG_(message)(Vg_DebugMsg,
|
|
"FATAL: signal delivered on the wrong stack?!");
|
|
VG_(message)(Vg_DebugMsg,
|
|
"A possible workaround follows. Please tell me");
|
|
VG_(message)(Vg_DebugMsg,
|
|
"(jseward@acm.org) if the suggested workaround doesn't help.");
|
|
VG_(unimplemented)
|
|
("support for progs compiled with -p/-pg; "
|
|
"rebuild your prog without -p/-pg");
|
|
}
|
|
|
|
vg_assert((Char*)(&(VG_(sigstack)[0])) <= (Char*)(&dummy_local));
|
|
vg_assert((Char*)(&dummy_local) < (Char*)(&(VG_(sigstack)[10000])));
|
|
|
|
VG_(block_all_host_signals)( &saved_procmask );
|
|
|
|
/* This is a sanity check. Either a signal has arrived because the
|
|
client set a handler for it, or because some thread sigwaited on
|
|
it. Establish that at least one of these is the case. */
|
|
sane = False;
|
|
if (vg_scss.scss_per_sig[sigNo].scss_handler != VKI_SIG_DFL
|
|
&& vg_scss.scss_per_sig[sigNo].scss_handler != VKI_SIG_IGN) {
|
|
sane = True;
|
|
} else {
|
|
for (tid = 1; tid < VG_N_THREADS; tid++) {
|
|
if (VG_(threads)[tid].status != VgTs_WaitSIG)
|
|
continue;
|
|
if (VG_(ksigismember)(&VG_(threads)[tid].sigs_waited_for, sigNo))
|
|
sane = True;
|
|
}
|
|
}
|
|
if (!sane) {
|
|
if (VG_(clo_trace_signals)) {
|
|
VG_(add_to_msg)("unexpected!");
|
|
VG_(end_msg)();
|
|
}
|
|
/* Note: we panic with all signals blocked here. Don't think
|
|
that matters. */
|
|
VG_(panic)("vg_oursignalhandler: unexpected signal");
|
|
}
|
|
/* End of the sanity check. */
|
|
|
|
/* Decide what to do with it. */
|
|
if (vg_dcss.dcss_sigpending[sigNo]) {
|
|
/* pending; ignore it. */
|
|
if (VG_(clo_trace_signals)) {
|
|
VG_(add_to_msg)("already pending; discarded" );
|
|
VG_(end_msg)();
|
|
}
|
|
} else {
|
|
/* Ok, we'd better deliver it to the client. */
|
|
/* Queue it up for delivery at some point in the future. */
|
|
vg_dcss.dcss_sigpending[sigNo] = True;
|
|
vg_dcss.dcss_destthread[sigNo] = VG_INVALID_THREADID;
|
|
if (VG_(clo_trace_signals)) {
|
|
VG_(add_to_msg)("queued" );
|
|
VG_(end_msg)();
|
|
}
|
|
}
|
|
|
|
/* We've finished messing with the queue, so re-enable host
|
|
signals. */
|
|
VG_(restore_all_host_signals)( &saved_procmask );
|
|
|
|
if (sigNo == VKI_SIGSEGV || sigNo == VKI_SIGBUS
|
|
|| sigNo == VKI_SIGFPE || sigNo == VKI_SIGILL) {
|
|
/* Can't continue; must longjmp back to the scheduler and thus
|
|
enter the sighandler immediately. */
|
|
VG_(longjmpd_on_signal) = sigNo;
|
|
__builtin_longjmp(VG_(scheduler_jmpbuf),1);
|
|
}
|
|
}
|
|
|
|
|
|
/* The outer insn loop calls here to reenable a host signal if
|
|
vg_oursighandler longjmp'd.
|
|
*/
|
|
void VG_(unblock_host_signal) ( Int sigNo )
|
|
{
|
|
Int ret;
|
|
vki_ksigset_t set;
|
|
VG_(ksigemptyset)(&set);
|
|
ret = VG_(ksigaddset)(&set,sigNo);
|
|
vg_assert(ret == 0);
|
|
ret = VG_(ksigprocmask)(VKI_SIG_UNBLOCK,&set,NULL);
|
|
vg_assert(ret == 0);
|
|
}
|
|
|
|
|
|
static __attribute((unused))
|
|
void pp_vg_ksigaction ( vki_ksigaction* sa )
|
|
{
|
|
Int i;
|
|
VG_(printf)("vg_ksigaction: handler %p, flags 0x%x, restorer %p\n",
|
|
sa->ksa_handler, (UInt)sa->ksa_flags, sa->ksa_restorer);
|
|
VG_(printf)("vg_ksigaction: { ");
|
|
for (i = 1; i <= VKI_KNSIG; i++)
|
|
if (VG_(ksigismember(&(sa->ksa_mask),i)))
|
|
VG_(printf)("%d ", i);
|
|
VG_(printf)("}\n");
|
|
}
|
|
|
|
|
|
/* At startup, copy the process' real signal state to the SCSS.
|
|
Whilst doing this, block all real signals. Then calculate SKSS and
|
|
set the kernel to that. Also initialise DCSS.
|
|
*/
|
|
void VG_(sigstartup_actions) ( void )
|
|
{
|
|
Int i, ret;
|
|
|
|
vki_ksigset_t saved_procmask;
|
|
vki_kstack_t altstack_info;
|
|
vki_ksigaction sa;
|
|
|
|
/* VG_(printf)("SIGSTARTUP\n"); */
|
|
/* Block all signals.
|
|
saved_procmask remembers the previous mask. */
|
|
VG_(block_all_host_signals)( &saved_procmask );
|
|
|
|
/* Copy per-signal settings to SCSS. */
|
|
for (i = 1; i <= VKI_KNSIG; i++) {
|
|
|
|
/* Get the old host action */
|
|
ret = VG_(ksigaction)(i, NULL, &sa);
|
|
vg_assert(ret == 0);
|
|
|
|
if (VG_(clo_trace_signals))
|
|
VG_(printf)("snaffling handler 0x%x for signal %d\n",
|
|
(Addr)(sa.ksa_handler), i );
|
|
|
|
vg_scss.scss_per_sig[i].scss_handler = sa.ksa_handler;
|
|
vg_scss.scss_per_sig[i].scss_flags = sa.ksa_flags;
|
|
vg_scss.scss_per_sig[i].scss_mask = sa.ksa_mask;
|
|
vg_scss.scss_per_sig[i].scss_restorer = sa.ksa_restorer;
|
|
}
|
|
|
|
/* Copy the alt stack, if any. */
|
|
ret = VG_(ksigaltstack)(NULL, &vg_scss.altstack);
|
|
vg_assert(ret == 0);
|
|
|
|
/* Copy the process' signal mask into the root thread. */
|
|
vg_assert(VG_(threads)[1].status == VgTs_Runnable);
|
|
VG_(threads)[1].sig_mask = saved_procmask;
|
|
|
|
/* Initialise DCSS. */
|
|
for (i = 1; i <= VKI_KNSIG; i++) {
|
|
vg_dcss.dcss_sigpending[i] = False;
|
|
vg_dcss.dcss_destthread[i] = VG_INVALID_THREADID;
|
|
}
|
|
|
|
/* Register an alternative stack for our own signal handler to run
|
|
on. */
|
|
altstack_info.ss_sp = &(VG_(sigstack)[0]);
|
|
altstack_info.ss_size = 10000 * sizeof(UInt);
|
|
altstack_info.ss_flags = 0;
|
|
ret = VG_(ksigaltstack)(&altstack_info, NULL);
|
|
if (ret != 0) {
|
|
VG_(panic)(
|
|
"vg_sigstartup_actions: couldn't install alternative sigstack");
|
|
}
|
|
if (VG_(clo_trace_signals)) {
|
|
VG_(message)(Vg_DebugExtraMsg,
|
|
"vg_sigstartup_actions: sigstack installed ok");
|
|
}
|
|
|
|
/* DEBUGGING HACK */
|
|
/* VG_(ksignal)(VKI_SIGUSR1, &VG_(oursignalhandler)); */
|
|
|
|
/* Calculate SKSS and apply it. This also sets the initial kernel
|
|
mask we need to run with. */
|
|
VG_(handle_SCSS_change)( True /* forced update */ );
|
|
}
|
|
|
|
|
|
/* Copy the process' sim signal state to the real state,
|
|
for when we transfer from the simulated to real CPU.
|
|
PROBLEM: what if we're running a signal handler when we
|
|
get here? Hmm.
|
|
I guess we wind up in vg_signalreturn_bogusRA, *or* the
|
|
handler has done/will do a longjmp, in which case we're ok.
|
|
|
|
It is important (see vg_startup.S) that this proc does not
|
|
change the state of the real FPU, since it is called when
|
|
running the program on the real CPU.
|
|
*/
|
|
void VG_(sigshutdown_actions) ( void )
|
|
{
|
|
Int i, ret;
|
|
|
|
vki_ksigset_t saved_procmask;
|
|
vki_ksigaction sa;
|
|
|
|
VG_(block_all_host_signals)( &saved_procmask );
|
|
|
|
/* Copy per-signal settings from SCSS. */
|
|
for (i = 1; i <= VKI_KNSIG; i++) {
|
|
|
|
sa.ksa_handler = vg_scss.scss_per_sig[i].scss_handler;
|
|
sa.ksa_flags = vg_scss.scss_per_sig[i].scss_flags;
|
|
sa.ksa_mask = vg_scss.scss_per_sig[i].scss_mask;
|
|
sa.ksa_restorer = vg_scss.scss_per_sig[i].scss_restorer;
|
|
|
|
if (VG_(clo_trace_signals))
|
|
VG_(printf)("restoring handler 0x%x for signal %d\n",
|
|
(Addr)(sa.ksa_handler), i );
|
|
|
|
/* Get the old host action */
|
|
ret = VG_(ksigaction)(i, &sa, NULL);
|
|
vg_assert(ret == 0);
|
|
|
|
}
|
|
|
|
/* Restore the sig alt stack. */
|
|
ret = VG_(ksigaltstack)(&vg_scss.altstack, NULL);
|
|
vg_assert(ret == 0);
|
|
|
|
/* A bit of a kludge -- set the sigmask to that of the root
|
|
thread. */
|
|
vg_assert(VG_(threads)[1].status != VgTs_Empty);
|
|
VG_(restore_all_host_signals)( &VG_(threads)[1].sig_mask );
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end vg_signals.c ---*/
|
|
/*--------------------------------------------------------------------*/
|