Add support for pthread_sigmask() and sigwait().  All absolutely
horrible, especially the latter.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@266 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c
index 5e6e14c..a7dfb70 100644
--- a/coregrind/arch/x86-linux/vg_libpthread.c
+++ b/coregrind/arch/x86-linux/vg_libpthread.c
@@ -617,6 +617,61 @@
 
 
 /* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+#include <signal.h>
+
+int pthread_sigmask(int how, const sigset_t *newmask, 
+                             sigset_t *oldmask)
+{
+   int res;
+
+   /* A bit subtle, because the scheduler expects newmask and oldmask
+      to be vki_sigset_t* rather than sigset_t*, and the two are
+      different.  Fortunately the first 64 bits of a sigset_t are
+      exactly a vki_sigset_t, so we just pass the pointers through
+      unmodified.  Haaaack! 
+
+      Also mash the how value so that the SIG_ constants from glibc
+      do not have to be included into vg_scheduler.c. */
+
+   ensure_valgrind("pthread_sigmask");
+
+   switch (how) {
+      case SIG_SETMASK: how = 1; break;
+      case SIG_BLOCK:   how = 2; break;
+      case SIG_UNBLOCK: how = 3; break;
+      default:          return EINVAL;
+   }
+
+   /* Crude check */
+   if (newmask == NULL)
+      return EFAULT;
+
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_SIGMASK,
+                           how, newmask, oldmask, 0);
+
+   /* The scheduler tells us of any memory violations. */
+   return res == 0 ? 0 : EFAULT;
+}
+
+
+int sigwait ( const sigset_t* set, int* sig )
+{
+   int res;
+   ensure_valgrind("sigwait");
+   /* As with pthread_sigmask we deliberately confuse sigset_t with
+      vki_ksigset_t. */
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__SIGWAIT,
+                           set, sig, 0, 0);
+   return res;
+}
+
+
+/* ---------------------------------------------------
    THREAD-SPECIFICs
    ------------------------------------------------ */
 
@@ -872,7 +927,6 @@
    ------------------------------------------------------------------ */
 
 #include <stdlib.h>
-#include <signal.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
diff --git a/coregrind/arch/x86-linux/vg_libpthread_unimp.c b/coregrind/arch/x86-linux/vg_libpthread_unimp.c
index c592e32..4157af6 100644
--- a/coregrind/arch/x86-linux/vg_libpthread_unimp.c
+++ b/coregrind/arch/x86-linux/vg_libpthread_unimp.c
@@ -150,7 +150,7 @@
 //void pthread_setcanceltype ( void )  { unimp("pthread_setcanceltype"); }
 //void pthread_setschedparam ( void )  { unimp("pthread_setschedparam"); }
 //void pthread_setspecific ( void )  { unimp("pthread_setspecific"); }
-void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
+//void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
 //void pthread_testcancel ( void )  { unimp("pthread_testcancel"); }
 void raise ( void )  { unimp("raise"); }
 void sem_close ( void )  { unimp("sem_close"); }
@@ -159,7 +159,7 @@
 void sem_unlink ( void )  { unimp("sem_unlink"); }
 //void sigaction ( void )  { unimp("sigaction"); }
 void siglongjmp ( void )  { unimp("siglongjmp"); }
-void sigwait ( void )  { unimp("sigwait"); }
+//void sigwait ( void )  { unimp("sigwait"); }
 
 #if 0
 void pthread_create@@GLIBC_2.1 ( void )  { unimp("pthread_create@@GLIBC_2.1"); }
diff --git a/coregrind/vg_errcontext.c b/coregrind/vg_errcontext.c
index aabbe43..6175339 100644
--- a/coregrind/vg_errcontext.c
+++ b/coregrind/vg_errcontext.c
@@ -288,7 +288,7 @@
       case Stack: 
          VG_(message)(Vg_UserMsg, 
                       "   Address 0x%x is on thread %d's stack", 
-                      ai->stack_tid, a);
+                      a, ai->stack_tid);
          break;
       case Unknown:
          if (ai->maybe_gcc) {
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index 765d719..d1cfca0 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -433,6 +433,8 @@
 #define VG_USERREQ__PTHREAD_SETSPECIFIC     0x300F
 #define VG_USERREQ__PTHREAD_GETSPECIFIC     0x3010
 #define VG_USERREQ__READ_MILLISECOND_TIMER  0x3011
+#define VG_USERREQ__PTHREAD_SIGMASK         0x3012
+#define VG_USERREQ__SIGWAIT                 0x3013
 
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
@@ -478,6 +480,7 @@
       VgTs_WaitFD,     /* waiting for I/O completion on a fd */
       VgTs_WaitMX,     /* waiting on a mutex */
       VgTs_WaitCV,     /* waiting on a condition variable */
+      VgTs_WaitSIG,    /* waiting due to sigwait() */
       VgTs_Sleeping    /* sleeping for a while */
    }
    ThreadStatus;
@@ -530,6 +533,19 @@
       /* thread-specific data */
       void* specifics[VG_N_THREAD_KEYS];
 
+      /* This thread's blocked-signals mask.  Semantics is that for a
+         signal to be delivered to this thread, the signal must not be
+         blocked by either the process-wide signal mask nor by this
+         one.  So, if this thread is prepared to handle any signal that
+         the process as a whole is prepared to handle, this mask should
+         be made empty -- and that it is its default, starting
+         state. */
+      vki_ksigset_t sig_mask;
+
+      /* When not VgTs_WaitSIG, has no meaning.  When VgTs_WaitSIG,
+         is the set of signals for which we are sigwait()ing. */
+      vki_ksigset_t sigs_waited_for;
+
       /* Stacks.  When a thread slot is freed, we don't deallocate its
          stack; we just leave it lying around for the next use of the
          slot.  If the next use of the slot requires a larger stack,
@@ -581,6 +597,9 @@
    ThreadState;
 
 
+/* Trivial range check on tid. */
+extern Bool VG_(is_valid_tid) ( ThreadId tid );
+
 /* Copy the specified thread's state into VG_(baseBlock) in
    preparation for running it. */
 extern void VG_(load_thread_state)( ThreadId );
@@ -591,6 +610,7 @@
 
 /* Get the thread state block for the specified thread. */
 extern ThreadState* VG_(get_thread_state)( ThreadId );
+extern ThreadState* VG_(get_thread_state_UNCHECKED)( ThreadId );
 
 /* And for the currently running one, if valid. */
 extern ThreadState* VG_(get_current_thread_state) ( void );
@@ -663,9 +683,10 @@
 
 extern void VG_(sigstartup_actions) ( void );
 
-extern Bool VG_(deliver_signals) ( ThreadId );
+extern Bool VG_(deliver_signals) ( void );
 extern void VG_(unblock_host_signal) ( Int sigNo );
-
+extern void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid );
+extern void VG_(update_sigstate_following_WaitSIG_change) ( void );
 
 /* Fake system calls for signal handling. */
 extern void VG_(do__NR_sigaction)     ( ThreadId tid );
@@ -797,9 +818,10 @@
    definitions, which are different in places from those that glibc
    defines.  Since we're operating right at the kernel interface,
    glibc's view of the world is entirely irrelevant. */
-extern Int VG_(ksigfillset)( vki_ksigset_t* set );
-extern Int VG_(ksigemptyset)( vki_ksigset_t* set );
-extern Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
+extern Int  VG_(ksigfillset)( vki_ksigset_t* set );
+extern Int  VG_(ksigemptyset)( vki_ksigset_t* set );
+extern Bool VG_(kisemptysigset)( vki_ksigset_t* set );
+extern Int  VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
 
 extern Int VG_(ksigprocmask)( Int how, const vki_ksigset_t* set, 
                                        vki_ksigset_t* oldset );
@@ -807,6 +829,11 @@
                              const vki_ksigaction* act,  
                              vki_ksigaction* oldact );
 extern Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum );
+extern void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+extern void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+
 
 extern Int VG_(ksignal)(Int signum, void (*sighandler)(Int));
 
diff --git a/coregrind/vg_kerneliface.h b/coregrind/vg_kerneliface.h
index a7690dd..b42ab44 100644
--- a/coregrind/vg_kerneliface.h
+++ b/coregrind/vg_kerneliface.h
@@ -71,6 +71,7 @@
    }
    vki_ksigset_t;
 
+
 typedef
    struct {
       void*         ksa_handler;
@@ -89,6 +90,8 @@
    vki_kstack_t;
 
 
+
+
 #define VKI_SIG_BLOCK          0    /* for blocking signals */
 #define VKI_SIG_UNBLOCK        1    /* for unblocking signals */
 #define VKI_SIG_SETMASK        2    /* for setting the signal mask */
@@ -297,14 +300,13 @@
 };
 
 
-/* To do with the ELF constructed by the kernel on a process' stack
-   just before it transfers control to the program's interpreter
-   (to use the ELF parlance).  
+/* To do with the ELF frame constructed by the kernel on a process'
+   stack just before it transfers control to the program's interpreter
+   (to use the ELF parlance).
    Constants from /usr/src/linux-2.4.9-31/include/linux/elf.h
    Logic from     /usr/src/linux-2.4.9-31/fs/binfmt_elf.c
                   and its counterpart in the 2.2.14 kernel sources 
-                  in Red Hat 6.2.
-*/
+                  in Red Hat 6.2.  */
 #define VKI_AT_CLKTCK 17    /* frequency at which times() increments */
 #define VKI_AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
 #define VKI_AT_BASE   7     /* base address of interpreter */
diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c
index 5e6e14c..a7dfb70 100644
--- a/coregrind/vg_libpthread.c
+++ b/coregrind/vg_libpthread.c
@@ -617,6 +617,61 @@
 
 
 /* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+#include <signal.h>
+
+int pthread_sigmask(int how, const sigset_t *newmask, 
+                             sigset_t *oldmask)
+{
+   int res;
+
+   /* A bit subtle, because the scheduler expects newmask and oldmask
+      to be vki_sigset_t* rather than sigset_t*, and the two are
+      different.  Fortunately the first 64 bits of a sigset_t are
+      exactly a vki_sigset_t, so we just pass the pointers through
+      unmodified.  Haaaack! 
+
+      Also mash the how value so that the SIG_ constants from glibc
+      do not have to be included into vg_scheduler.c. */
+
+   ensure_valgrind("pthread_sigmask");
+
+   switch (how) {
+      case SIG_SETMASK: how = 1; break;
+      case SIG_BLOCK:   how = 2; break;
+      case SIG_UNBLOCK: how = 3; break;
+      default:          return EINVAL;
+   }
+
+   /* Crude check */
+   if (newmask == NULL)
+      return EFAULT;
+
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_SIGMASK,
+                           how, newmask, oldmask, 0);
+
+   /* The scheduler tells us of any memory violations. */
+   return res == 0 ? 0 : EFAULT;
+}
+
+
+int sigwait ( const sigset_t* set, int* sig )
+{
+   int res;
+   ensure_valgrind("sigwait");
+   /* As with pthread_sigmask we deliberately confuse sigset_t with
+      vki_ksigset_t. */
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__SIGWAIT,
+                           set, sig, 0, 0);
+   return res;
+}
+
+
+/* ---------------------------------------------------
    THREAD-SPECIFICs
    ------------------------------------------------ */
 
@@ -872,7 +927,6 @@
    ------------------------------------------------------------------ */
 
 #include <stdlib.h>
-#include <signal.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
diff --git a/coregrind/vg_libpthread_unimp.c b/coregrind/vg_libpthread_unimp.c
index c592e32..4157af6 100644
--- a/coregrind/vg_libpthread_unimp.c
+++ b/coregrind/vg_libpthread_unimp.c
@@ -150,7 +150,7 @@
 //void pthread_setcanceltype ( void )  { unimp("pthread_setcanceltype"); }
 //void pthread_setschedparam ( void )  { unimp("pthread_setschedparam"); }
 //void pthread_setspecific ( void )  { unimp("pthread_setspecific"); }
-void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
+//void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
 //void pthread_testcancel ( void )  { unimp("pthread_testcancel"); }
 void raise ( void )  { unimp("raise"); }
 void sem_close ( void )  { unimp("sem_close"); }
@@ -159,7 +159,7 @@
 void sem_unlink ( void )  { unimp("sem_unlink"); }
 //void sigaction ( void )  { unimp("sigaction"); }
 void siglongjmp ( void )  { unimp("siglongjmp"); }
-void sigwait ( void )  { unimp("sigwait"); }
+//void sigwait ( void )  { unimp("sigwait"); }
 
 #if 0
 void pthread_create@@GLIBC_2.1 ( void )  { unimp("pthread_create@@GLIBC_2.1"); }
diff --git a/coregrind/vg_mylibc.c b/coregrind/vg_mylibc.c
index f42ba94..4b4c8f3 100644
--- a/coregrind/vg_mylibc.c
+++ b/coregrind/vg_mylibc.c
@@ -154,6 +154,15 @@
    return 0;
 }
 
+Bool VG_(kisemptysigset)( vki_ksigset_t* set )
+{
+   Int i;
+   vg_assert(set != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      if (set->ws[i] != 0x0) return False;
+   return True;
+}
+
 Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
@@ -168,9 +177,9 @@
 Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
-      return -1;
+      return 0;
    if (signum < 1 && signum > VKI_KNSIG)
-      return -1;
+      return 0;
    signum--;
    if (1 & ((set->ws[signum / VKI_KNSIG_BPW]) >> (signum % VKI_KNSIG_BPW)))
       return 1;
@@ -179,6 +188,25 @@
 }
 
 
+/* Add all signals in src to dst. */
+void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] |= src->ws[i];
+}
+
+/* Remove all signals in src from dst. */
+void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] &= ~(src->ws[i]);
+}
+
+
 /* The functions sigaction, sigprocmask, sigpending and sigsuspend
    return 0 on success and -1 on error.  
 */
diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c
index 53edabb..d50fe93 100644
--- a/coregrind/vg_scheduler.c
+++ b/coregrind/vg_scheduler.c
@@ -30,7 +30,6 @@
 
 #include "vg_include.h"
 #include "vg_constants.h"
-
 #include "valgrind.h" /* for VG_USERREQ__MAKE_NOACCESS and
                          VG_USERREQ__DO_LEAK_CHECK */
 
@@ -156,8 +155,8 @@
    Helper functions for the scheduler.
    ------------------------------------------------------------------ */
 
-static __inline__
-Bool is_valid_tid ( ThreadId tid )
+__inline__
+Bool VG_(is_valid_tid) ( ThreadId tid )
 {
    /* tid is unsigned, hence no < 0 test. */
    if (tid == 0) return False;
@@ -216,6 +215,7 @@
          case VgTs_Sleeping:   VG_(printf)("Sleeping"); break;
          case VgTs_WaitMX:     VG_(printf)("WaitMX"); break;
          case VgTs_WaitCV:     VG_(printf)("WaitCV"); break;
+         case VgTs_WaitSIG:    VG_(printf)("WaitSIG"); break;
          default: VG_(printf)("???"); break;
       }
       VG_(printf)(", associated_mx = %p, associated_cv = %p\n", 
@@ -340,9 +340,16 @@
 }
 
 
+ThreadState* VG_(get_thread_state_UNCHECKED) ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   return & vg_threads[tid];
+}
+
+
 ThreadState* VG_(get_thread_state) ( ThreadId tid )
 {
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
    return & vg_threads[tid];
 }
@@ -461,7 +468,7 @@
 UInt run_thread_for_a_while ( ThreadId tid )
 {
    volatile UInt trc = 0;
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
    vg_assert(VG_(bbs_to_go) > 0);
 
@@ -541,6 +548,8 @@
       vg_threads[i].stack_size = 0;
       vg_threads[i].stack_base = (Addr)NULL;
       vg_threads[i].tid        = i;
+      VG_(ksigemptyset)(&vg_threads[i].sig_mask);
+      VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
    }
 
    for (i = 0; i < VG_N_WAITING_FDS; i++)
@@ -714,7 +723,7 @@
 {
    Int  i, waiters;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_WaitFD);
    vg_assert(vg_threads[tid].m_eax == __NR_read 
              || vg_threads[tid].m_eax == __NR_write);
@@ -744,7 +753,7 @@
    Char msg_buf[100];
    Bool restart_blocked_syscalls;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    restart_blocked_syscalls = VG_(signal_returns)(tid);
 
@@ -793,7 +802,7 @@
    Bool orig_fd_blockness;
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    syscall_no = vg_threads[tid].m_eax; /* syscall number */
@@ -969,7 +978,7 @@
       if (fd > fd_max) 
          fd_max = fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
       syscall_no = vg_waiting_fds[i].syscall_no;
       switch (syscall_no) {
          case __NR_read:
@@ -1075,7 +1084,7 @@
 
       fd  = vg_waiting_fds[i].fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
 
       /* The thread actually has to be waiting for the I/O event it
          requested before we can deliver the result! */
@@ -1214,7 +1223,7 @@
             even if, as a result, it has missed the unlocking of it.
             Potential deadlock.  This sounds all very strange, but the
             POSIX standard appears to require this behaviour.  */
-         sigs_delivered = VG_(deliver_signals)( 1 /*HACK*/ );
+         sigs_delivered = VG_(deliver_signals)();
 	 if (sigs_delivered)
             VG_(do_sanity_checks)( False );
 
@@ -1226,6 +1235,7 @@
             if (tid_next >= VG_N_THREADS) tid_next = 1;
             if (vg_threads[tid_next].status == VgTs_WaitFD
                 || vg_threads[tid_next].status == VgTs_Sleeping
+                || vg_threads[tid_next].status == VgTs_WaitSIG
                 || (vg_threads[tid_next].status == VgTs_WaitCV 
                     && vg_threads[tid_next].awaken_at != 0xFFFFFFFF))
                n_in_bounded_wait ++;
@@ -1257,7 +1267,7 @@
             thread becomes runnable. */
          nanosleep_for_a_while();
 	 /* pp_sched_status(); */
-	 /* VG_(printf)(".\n"); */
+	 /* VG_(printf)("."); */
       }
 
 
@@ -1524,16 +1534,37 @@
    Thread CREATION, JOINAGE and CANCELLATION.
    -------------------------------------------------------- */
 
+/* Release resources and generally clean up once a thread has finally
+   disappeared. */
+static
+void cleanup_after_thread_exited ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   vg_assert(vg_threads[tid].status == VgTs_Empty);
+   /* Mark its stack no-access */
+   if (VG_(clo_instrument) && tid != 1)
+      VGM_(make_noaccess)( vg_threads[tid].stack_base,
+                           vg_threads[tid].stack_size );
+   /* Forget about any pending signals directed specifically at this
+      thread. */
+   VG_(notify_signal_machinery_of_thread_exit)( tid );
+
+   /* Get rid of signal handlers specifically arranged for this
+      thread. */
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 static
 void do_pthread_cancel ( ThreadId  tid,
                          pthread_t tid_cancellee )
 {
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
-   if (!is_valid_tid(tid_cancellee)
+   if (!VG_(is_valid_tid)(tid_cancellee)
        || vg_threads[tid_cancellee].status == VgTs_Empty) {
       SET_EDX(tid, ESRCH);
       return;
@@ -1588,7 +1619,7 @@
 
    /* Mark it as not in use.  Leave the stack in place so the next
       user of this slot doesn't reallocate it. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
    vg_threads[tid].retval = retval;
@@ -1611,7 +1642,7 @@
          call.  TODO: free properly the slot (also below). 
       */
       jnr = vg_threads[tid].joiner;
-      vg_assert(is_valid_tid(jnr));
+      vg_assert(VG_(is_valid_tid)(jnr));
       vg_assert(vg_threads[jnr].status == VgTs_WaitJoinee);
       jnr_args = (UInt*)vg_threads[jnr].m_eax;
       jnr_thread_return = (void**)(jnr_args[2]);
@@ -1620,9 +1651,7 @@
       SET_EDX(jnr, 0); /* success */
       vg_threads[jnr].status = VgTs_Runnable;
       vg_threads[tid].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && tid != 0)
-         VGM_(make_noaccess)( vg_threads[tid].stack_base,
-                              vg_threads[tid].stack_size );
+      cleanup_after_thread_exited ( tid );
       if (VG_(clo_trace_sched)) {
          VG_(sprintf)(msg_buf, 
             "root fn returns, to find a waiting pthread_join(%d)", tid);
@@ -1645,7 +1674,7 @@
 
    /* jee, the joinee, is the thread specified as an arg in thread
       tid's call to pthread_join.  So tid is the join-er. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    if (jee == tid) {
@@ -1687,9 +1716,7 @@
       }
       vg_threads[tid].status = VgTs_Runnable;
       vg_threads[jee].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && jee != 0)
-         VGM_(make_noaccess)( vg_threads[jee].stack_base,
-                              vg_threads[jee].stack_size );
+      cleanup_after_thread_exited ( jee );
       if (VG_(clo_trace_sched)) {
 	 VG_(sprintf)(msg_buf,
 		      "someone called pthread_join() on me; bye!");
@@ -1736,7 +1763,7 @@
 
    /* If we've created the main thread's tid, we're in deep trouble :) */
    vg_assert(tid != 1);
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    /* Copy the parent's CPU state into the child's, in a roundabout
       way (via baseBlock). */
@@ -1807,6 +1834,10 @@
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
       vg_threads[tid].specifics[i] = NULL;
 
+   /* We inherit our parent's signal mask. (?!) */
+   vg_threads[tid].sig_mask = vg_threads[parent_tid].sig_mask;
+   VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
+
    /* return zero */
    SET_EDX(parent_tid, 0); /* success */
 }
@@ -1920,7 +1951,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    /* POSIX doesn't mandate this, but for sanity ... */
@@ -1951,7 +1982,7 @@
 
    if (mutex->__m_count > 0) {
 
-      vg_assert(is_valid_tid((ThreadId)mutex->__m_owner));
+      vg_assert(VG_(is_valid_tid)((ThreadId)mutex->__m_owner));
 
       /* Someone has it already. */
       if ((ThreadId)mutex->__m_owner == tid) {
@@ -2018,7 +2049,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL) {
@@ -2109,7 +2140,7 @@
    pthread_mutex_t* mx;
    pthread_cond_t*  cv;
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_WaitCV
              && vg_threads[tid].awaken_at != 0xFFFFFFFF);
    mx = vg_threads[tid].associated_mx;
@@ -2238,7 +2269,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL || cond == NULL) {
@@ -2306,7 +2337,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (cond == NULL) {
@@ -2352,7 +2383,7 @@
    }
 
    vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey));
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
@@ -2388,7 +2419,7 @@
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
    
    if (!is_valid_key(key)) {
@@ -2420,7 +2451,7 @@
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2444,7 +2475,7 @@
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2457,6 +2488,85 @@
 }
 
 
+/* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+/* See comment in vg_libthread.c:pthread_sigmask() regarding
+   deliberate confusion of types sigset_t and vki_sigset_t.  Also re
+   meaning of the mashed_how value.  Return 0 for OK and 1 for some
+   kind of addressing error, which the vg_libpthread.c routine turns
+   into return values 0 and EFAULT respectively. */
+static
+void do_pthread_sigmask ( ThreadId tid,
+                          Int mashed_how,
+                          vki_ksigset_t* newmask, 
+                          vki_ksigset_t* oldmask )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_pthread_level) >= 1) {
+      VG_(sprintf)(msg_buf, 
+         "pthread_sigmask          m_how %d, newmask %p, oldmask %p",
+         mashed_how, newmask, oldmask );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   if (VG_(clo_instrument)) {
+      /* TODO check newmask/oldmask are addressible/defined */
+   }
+
+   if (oldmask != NULL) {
+      *oldmask = vg_threads[tid].sig_mask;
+      if (VG_(clo_instrument)) {
+         VGM_(make_readable)( (Addr)oldmask, sizeof(vki_ksigset_t) );
+      }
+   }
+
+   switch (mashed_how) {
+      case 1: /* SIG_SETMASK */
+         vg_threads[tid].sig_mask = *newmask;
+         break;
+      case 2: /* SIG_BLOCK */
+         VG_(ksigaddset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      case 3: /* SIG_UNBLOCK */
+         VG_(ksigdelset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      default: 
+        VG_(panic)("do_pthread_sigmask: invalid mashed_how");
+        /*NOTREACHED*/
+        break;
+   }
+
+   SET_EDX(tid, 0);
+}
+
+
+static
+void do_sigwait ( ThreadId tid,
+                  vki_ksigset_t* set, 
+                  Int* sig )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_signals) || VG_(clo_trace_sched)) {
+      VG_(sprintf)(msg_buf, 
+         "suspend due to sigwait(): set %p, sig %p",
+         set, sig );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   vg_threads[tid].sigs_waited_for = *set;
+   vg_threads[tid].status = VgTs_WaitSIG;
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 /* ---------------------------------------------------------------------
    Handle non-trivial client requests.
    ------------------------------------------------------------------ */
@@ -2537,6 +2647,19 @@
 				  (void*)(arg[2]) );
  	 break;
 
+      case VG_USERREQ__PTHREAD_SIGMASK:
+         do_pthread_sigmask ( tid,
+                              arg[1],
+                              (vki_ksigset_t*)(arg[2]),
+                              (vki_ksigset_t*)(arg[3]) );
+	 break;
+
+      case VG_USERREQ__SIGWAIT:
+         do_sigwait ( tid,
+                      (vki_ksigset_t*)(arg[1]),
+                      (Int*)(arg[2]) );
+	 break;
+
       case VG_USERREQ__MAKE_NOACCESS:
       case VG_USERREQ__MAKE_WRITABLE:
       case VG_USERREQ__MAKE_READABLE:
@@ -2595,7 +2718,7 @@
          vg_assert(cv == NULL);
          /* 1 */ vg_assert(mx != NULL);
 	 /* 2 */ vg_assert(mx->__m_count > 0);
-         /* 3 */ vg_assert(is_valid_tid((ThreadId)mx->__m_owner));
+         /* 3 */ vg_assert(VG_(is_valid_tid)((ThreadId)mx->__m_owner));
          /* 4 */ vg_assert(i != (ThreadId)mx->__m_owner); 
       } else 
       if (vg_threads[i].status == VgTs_WaitCV) {
@@ -2626,6 +2749,15 @@
                "VG_PTHREAD_STACK_SIZE in vg_include.h and recompile.");
             VG_(exit)(1);
 	 }
+
+         if (vg_threads[i].status == VgTs_WaitSIG) {
+            vg_assert( ! VG_(kisemptysigset)(
+                            & vg_threads[i].sigs_waited_for) );
+	 } else {
+            vg_assert( VG_(kisemptysigset)(
+                          & vg_threads[i].sigs_waited_for) );
+	 }
+
       }
    }
 
diff --git a/coregrind/vg_signals.c b/coregrind/vg_signals.c
index cbc72d3..3edff21 100644
--- a/coregrind/vg_signals.c
+++ b/coregrind/vg_signals.c
@@ -39,33 +39,83 @@
    Signal state for this process.
    ------------------------------------------------------------------ */
 
-/* For each signal, the current action.  Is NULL if the client hasn't
-   asked to handle the signal.  Consequently, we expect never to
-   receive a signal for which the corresponding handler is NULL. */
-void* VG_(sighandler)[VKI_KNSIG];
+/* 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. 
+ */
+
+/* For each signal, the current action.  Either:
+
+   -- VG_SH_NOHANDLER if the client hasn't asked to handle the signal, 
+      and we havent surreptitiously installed any handler ourselves.
+
+   -- VG_SH_FAKEHANDLER if the client hasn't asked to handle the signal
+      directly, but has so indirectly via a sigwait() request.  In this
+      case we may need to install our own handler to catch signals which
+      the sigwait-mask for some thread will accept, but for which the
+      client hasn't actually installed a handler.  These "fake" handlers
+      are invisible to the client, so we need to be able to distinguish
+      this case so that we can fake a suitable response if the client
+      should enquire about the state of this signal using sigaction.
+
+   -- Otherwise, the client has installed a signal handler, and this
+      is the pointer to it.
+
+   Invariant: we never expect to receive a signal for which the 
+   vg_sighandler[] entry is VG_SH_NOHANDLER.  If it is VG_SH_FAKEHANDLER
+   we know that we should look for a thread in VgTs_WaitSIG state to
+   release.  Otherwise, we find a thread capable of handling this
+   signal and run the specified handler on it.
+*/
+#define VG_SH_NOHANDLER    ((void*)0)
+#define VG_SH_FAKEHANDLER  ((void*)1)
+
+void* vg_sighandler[1+VKI_KNSIG];
 
 
 /* For each signal, either:
-   -- VG_SIGIDLE if not pending and not running
-   -- Handler address if pending
-   -- VG_SIGRUNNING if the handler is running and hasn't (returned or 
+   -- VG_SP_SIGIDLE if not pending and not running
+   -- Handler address if pending AND real handler
+   -- VG_SH_FAKEHANDLER if pending for sigwait
+   -- VG_SP_SIGRUNNING if the handler is running and hasn't (returned or 
       unblocked the signal using sigprocmask following a longjmp out 
       of the handler).
  */
-#define VG_SIGIDLE    ((void*)0)
-#define VG_SIGRUNNING ((void*)1)
+#define VG_SP_SIGIDLE    ((void*)0)
+#define VG_SP_SIGRUNNING ((void*)2)
 
-void* VG_(sigpending)[VKI_KNSIG];
+static
+void* vg_sigpending[1+VKI_KNSIG];
 
 
-/* For each signal that we have a handler for (ie, for those for which
-   the VG_(sighandler) entry is non-NULL), record whether or not the
-   client asked for syscalls to be restartable (SA_RESTART) if
-   interrupted by this signal.  We need to consult this when a signal
-   returns, if it should happen that the signal which we delivered has
-   interrupted a system call. */
+/* For each signal, the thread id to which the signal should be
+   delivered.  This is only meaningful if the corresponding
+   vg_sigpending entry actually points to a handler, ie, the signal
+   is pending.
+
+   In this case, the value VG_INVALID_THREADID indicates the signal is
+   not directed at a specific thread and so should be delivered to any
+   thread whose signal mask (ThreadState.sig_mask) field allows it.
+
+   Any other value indicates that the signal should be delivered only
+   to that specific thread, as some point in time when the thread has
+   not blocked the signal.  It remains pending until then. */
+static
+ThreadId vg_sig_threadid[1+VKI_KNSIG];
+
+
+/* For each signal that the client installed a handler for (ie, for
+   those for which the vg_sighandler entry is non-VG_SH_NOHANDLER and
+   non-VG_SH_FAKEHANDLER), record whether or not the client asked for
+   syscalls to be restartable (SA_RESTART) if interrupted by this
+   signal.  We need to consult this when a signal returns, if it
+   should happen that the signal which we delivered has interrupted a
+   system call. */
 static 
-Bool vg_sig_sarestart[VKI_KNSIG];
+Bool vg_sig_sarestart[1+VKI_KNSIG];
 
 
 /* ---------------------------------------------------------------------
@@ -179,7 +229,7 @@
 
    /* Set the thread so it will next run the handler. */
    tst->m_esp  = esp;
-   tst->m_eip  = (Addr)VG_(sigpending)[sigNo];
+   tst->m_eip  = (Addr)vg_sigpending[sigNo];
    /* This thread needs to be marked runnable, but we leave that the
       caller to do. */
 
@@ -218,7 +268,8 @@
    vg_assert(frame->magicPI == 0x31415927);
    vg_assert(frame->magicE  == 0x27182818);
    if (VG_(clo_trace_signals))
-      VG_(message)(Vg_DebugMsg, "vg_pop_signal_frame: valid magic");
+      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++)
@@ -276,7 +327,7 @@
    /* You would have thought that the following assertion made sense
       here:
 
-         vg_assert(vg_sigpending[sigNo] == VG_SIGRUNNING);
+         vg_assert(vg_sigpending[sigNo] == VG_SP_SIGRUNNING);
 
       Alas, you would be wrong.  If a sigprocmask has been intercepted
       and it unblocks this signal, then vg_sigpending[sigNo] will
@@ -291,8 +342,8 @@
       Ho Hum.  This seems like a race condition which surely isn't
       handled correctly.  */
 
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
-   VG_(sigpending)[sigNo] = VG_SIGIDLE;
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
+   vg_sigpending[sigNo] = VG_SP_SIGIDLE;
 
    /* Unlock and return. */
    VG_(restore_host_signals)( &saved_procmask );
@@ -306,12 +357,14 @@
 
 /* Deliver all pending signals, by building stack frames for their
    handlers.  Return True if any signals were delivered. */
-Bool VG_(deliver_signals) ( ThreadId tid )
+Bool VG_(deliver_signals) ( void )
 {
    vki_ksigset_t  saved_procmask;
    Int            sigNo;
    Bool           found;
- 
+   ThreadState*   tst;
+   ThreadId       tid;
+
    /* A cheap check.  We don't need to have exclusive access
       to the queue, because in the worst case, vg_oursignalhandler
       will add signals, causing us to return, thinking there
@@ -319,9 +372,10 @@
       A subsequent call here will handle the signal(s) we missed.
    */
    found = False;
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++)
-      if (VG_(sigpending)[sigNo] != VG_SIGIDLE &&
-          VG_(sigpending)[sigNo] != VG_SIGRUNNING) found = True;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++)
+      if (vg_sigpending[sigNo] != VG_SP_SIGIDLE 
+          && vg_sigpending[sigNo] != VG_SP_SIGRUNNING) 
+         found = True;
 
    if (!found) return False;
 
@@ -332,22 +386,98 @@
    VG_(block_all_host_signals)( &saved_procmask );
 
    /* Look for signals to deliver ... */
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++) {
-      if (VG_(sigpending)[sigNo] == VG_SIGIDLE ||
-          VG_(sigpending)[sigNo] == VG_SIGRUNNING) continue;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING) 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_(get_thread_state_UNCHECKED)(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_(get_thread_state)(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));
+         }
+	 tst->m_edx = 0;
+         tst->sh_edx = VGM_WORD_VALID;
+         tst->status = VgTs_Runnable;
+         VG_(update_sigstate_following_WaitSIG_change)();
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         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 so the
+         handler better be valid. */
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] != VG_SH_FAKEHANDLER);
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGRUNNING);
+
+      tid = vg_sig_threadid[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_(get_thread_state_UNCHECKED)(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_sigpending[sigNo] = VG_SP_SIGIDLE;
+            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_(get_thread_state_UNCHECKED)(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", sigNo );
+         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_assert(VG_(get_thread_state)(tid)->status != VgTs_Empty);
       vg_push_signal_frame ( tid, sigNo );
       VG_(get_thread_state)(tid)->status = VgTs_Runnable;
       
       /* Signify that the signal has been delivered. */
-      VG_(sigpending)[sigNo] = VG_SIGRUNNING;
+      vg_sigpending[sigNo] = VG_SP_SIGRUNNING;
    }
 
    /* Unlock and return. */
@@ -356,12 +486,36 @@
 }
 
 
+/* A thread is about to exit.  Forget about any signals which are
+   still pending for it. */
+void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid )
+{
+   Int sigNo;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING)
+         continue;
+      if (vg_sig_threadid[sigNo] == tid) {
+         /* sigNo is pending for tid, which is just about to disappear.
+            So forget about the pending signal. */
+         vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         if (VG_(clo_trace_signals)) 
+            VG_(message)(Vg_DebugMsg, 
+                "discarding pending signal %d due to thread %d exiting",
+                sigNo, tid );
+      }   
+   }
+}
+
+
 /* 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 )
+static 
+void VG_(oursignalhandler) ( Int sigNo )
 {
    Int           dummy_local;
    vki_ksigset_t saved_procmask;
@@ -381,7 +535,7 @@
       VG_(start_msg)(Vg_DebugMsg);
       VG_(add_to_msg)("signal %d arrived ... ", sigNo );
    }
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
 
    /* Sanity check.  Ensure we're really running on the signal stack
       we asked for. */
@@ -407,7 +561,7 @@
 
    VG_(block_all_host_signals)( &saved_procmask );
 
-   if (VG_(sighandler)[sigNo] == NULL) {
+   if (vg_sighandler[sigNo] == VG_SH_NOHANDLER) {
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("unexpected!");
          VG_(end_msg)();
@@ -418,7 +572,7 @@
    }
 
    /* Decide what to do with it. */
-   if (VG_(sigpending)[sigNo] == VG_SIGRUNNING) {
+   if (vg_sigpending[sigNo] == VG_SP_SIGRUNNING) {
        /* Already running; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already running; discarded" );
@@ -426,8 +580,8 @@
       }
    }
    else
-   if (VG_(sigpending)[sigNo] != VG_SIGRUNNING && 
-       VG_(sigpending)[sigNo] != VG_SIGIDLE) {
+   if (vg_sigpending[sigNo] != VG_SP_SIGRUNNING 
+       && vg_sigpending[sigNo] != VG_SP_SIGIDLE) {
       /* Not running and not idle == pending; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already pending; discarded" );
@@ -436,9 +590,11 @@
    } 
    else {
       /* Ok, we'd better deliver it to the client. */
-      vg_assert(VG_(sigpending)[sigNo] == VG_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] == VG_SP_SIGIDLE);
       /* Queue it up for delivery at some point in the future. */
-      VG_(sigpending)[sigNo] = VG_(sighandler)[sigNo];
+      vg_assert(vg_sighandler[sigNo] != VG_SH_NOHANDLER);
+      vg_sigpending[sigNo] = vg_sighandler[sigNo];
+      vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("queued" );
          VG_(end_msg)();
@@ -481,7 +637,7 @@
    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++)
+   for (i = 1; i <= VKI_KNSIG; i++)
       if (VG_(ksigismember(&(sa->ksa_mask),i)))
          VG_(printf)("%d ", i);
    VG_(printf)("}\n");
@@ -520,13 +676,14 @@
    }
 
    /* Set initial state for the signal simulation. */
-   for (i = 1; i < VKI_KNSIG; i++) {
-      VG_(sighandler)[i] = NULL;
-      VG_(sigpending)[i] = NULL;
+   for (i = 1; i <= VKI_KNSIG; i++) {
+      vg_sighandler[i] = VG_SH_NOHANDLER;
+      vg_sigpending[i] = VG_SP_SIGIDLE;
       vg_sig_sarestart[i] = True; /* An easy default */
+      vg_sig_threadid[i] = VG_INVALID_THREADID;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
 
       /* Get the old host action */
       ret = VG_(ksigaction)(i, NULL, &sa);
@@ -534,7 +691,8 @@
 
       /* If there's already a handler set, record it, then route the
          signal through to our handler. */
-      if (sa.ksa_handler != VKI_SIG_IGN && sa.ksa_handler != VKI_SIG_DFL) {
+      if (sa.ksa_handler != VKI_SIG_IGN 
+          && sa.ksa_handler != VKI_SIG_DFL) {
          if (VG_(clo_trace_signals))
             VG_(printf)("snaffling handler 0x%x for signal %d\n", 
                         (Addr)(sa.ksa_handler), i );
@@ -542,7 +700,7 @@
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
 
-         VG_(sighandler)[i] = sa.ksa_handler;
+         vg_sighandler[i] = sa.ksa_handler;
          sa.ksa_handler = &VG_(oursignalhandler);
 	 /* Save the restart status, then set it to restartable. */
 	 vg_sig_sarestart[i] 
@@ -585,12 +743,13 @@
    /* copy the sim signal actions to the real ones. */
    /* Hmm, this isn't accurate.  Doesn't properly restore the
       SA_RESTART flag nor SA_ONSTACK. */
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       if (i == VKI_SIGKILL || i == VKI_SIGSTOP) continue;
-      if (VG_(sighandler)[i] == NULL) continue;
+      if (vg_sighandler[i] == VG_SH_NOHANDLER 
+          || vg_sighandler[i] == VG_SH_FAKEHANDLER) continue;
       ret = VG_(ksigaction)(i, NULL, &sa);
       vg_assert(ret == 0);
-      sa.ksa_handler = VG_(sighandler)[i];
+      sa.ksa_handler = vg_sighandler[i];
       ret = VG_(ksigaction)(i, &sa, NULL);      
    }
 
@@ -598,6 +757,89 @@
 }
 
 
+void VG_(update_sigstate_following_WaitSIG_change) ( void )
+{
+   ThreadId      tid;
+   Int           sig;
+   vki_ksigset_t global_waitsigs;
+   ThreadState*  tst;
+
+   VG_(ksigemptyset)( &global_waitsigs );
+
+   /* Calculate the new set of signals which are being sigwait()d for
+      by at least one thread. */
+   for (tid = 1; tid < VG_N_THREADS; tid++) {
+      tst = VG_(get_thread_state_UNCHECKED)(tid);
+      if (tst->status != VgTs_WaitSIG)
+         continue;
+      vg_assert(! VG_(kisemptysigset)(
+                     & tst->sigs_waited_for ));
+      VG_(ksigaddset_from_set)( & global_waitsigs, 
+                                & tst->sigs_waited_for );
+   }
+
+   /* Now adjust vg_sighandler accordingly.
+
+      For each signal s: (lapses into pseudo-Haskell ...)
+
+      if s `elem` global_waitsigs[s]
+       -- at least one thread is sigwait()ing for s.  That means that at 
+          least _some_ kind of handler is needed.
+       case vg_sighandler[s] of
+          VG_SH_NOHANDLER -> install our own handler and set waitsigs[s] 
+             to VG_SH_FAKEHANDLER 
+          VG_SH_FAKEHANDLER -> there's already a handler.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+
+      if s `notElem` global_waitsigs[s]
+       -- we're not sigwait()ing for s (any longer).  
+       case vg_sighandler[s] of
+          VG_SH_FAKEHANDLER -> there is a handler installed, but ONLY for 
+             the purposes of handling sigwait().  So set it back to 
+             VG_SH_NOHANDLER and tell the kernel that we want to do the 
+             default action for s from now on, ie, we wish to deregister 
+             OUR handle.
+          VG_SH_NOHANDLER -> there was no handler anyway.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+ 
+   */
+
+   for (sig = 1; sig <= VKI_KNSIG; sig++) {
+      if (VG_(ksigismember)( & global_waitsigs, sig )) {
+         if (vg_sighandler[sig] == VG_SH_NOHANDLER
+             /* && existing kernel handler is SIG_DFL */) {
+            /* add handler */
+            /* We really only ought to do this if the existing kernel
+               handler is SIG_DFL.  That's because when queried by the
+               client's sigaction, that's what we claim it is if a fake
+               handler has been installed.  Or (perhaps better) 
+               remember the kernel's setting. 
+            */
+            VG_(ksignal)( sig, &VG_(oursignalhandler) );
+            vg_sighandler[sig] = VG_SH_FAKEHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "adding fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      } else {
+         if (vg_sighandler[sig] == VG_SH_FAKEHANDLER) {
+            /* remove handler */
+            VG_(ksignal)( sig, VKI_SIG_DFL);
+            vg_sighandler[sig] = VG_SH_NOHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "removing fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      }
+   }
+}
+
 /* ---------------------------------------------------------------------
    Handle signal-related syscalls from the simulatee.
    ------------------------------------------------------------------ */
@@ -628,7 +870,7 @@
       the call is passed to the kernel it will definitely succeed. */
 
    /* Reject out-of-range signal numbers. */
-   if (param1 < 1 || param1 >= VKI_KNSIG) goto bad_signo;
+   if (param1 < 1 || param1 > VKI_KNSIG) goto bad_signo;
 
    /* Reject attempts to set a handler (or set ignore) for SIGKILL. */
    if ( (param1 == VKI_SIGKILL || param1 == VKI_SIGSTOP)
@@ -636,14 +878,14 @@
        && new_action->ksa_handler != VKI_SIG_DFL)
       goto bad_sigkill_or_sigstop;
 
-   our_old_handler = VG_(sighandler)[param1];
+   our_old_handler = vg_sighandler[param1];
    /* VG_(printf)("old handler = 0x%x\n", our_old_handler); */
    /* If a new handler has been specified, mess with its handler. */
    if (new_action) {
       if (new_action->ksa_handler == VKI_SIG_IGN ||
           new_action->ksa_handler == VKI_SIG_DFL) {
-         VG_(sighandler)[param1] = NULL; 
-         VG_(sigpending)[param1] = NULL;
+         vg_sighandler[param1] = VG_SH_NOHANDLER;
+         vg_sigpending[param1] = VG_SP_SIGIDLE;
          /* Dangerous!  Could lose signals like this. */
       } else {
          /* VG_(printf)("new handler = 0x%x\n", new_action->ksa_handler); */
@@ -653,7 +895,7 @@
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
          new_action->ksa_flags |= VKI_SA_ONSTACK;
-         VG_(sighandler)[param1] = new_action->ksa_handler;
+         vg_sighandler[param1] = new_action->ksa_handler;
 	 vg_sig_sarestart[param1] 
             = (new_action->ksa_flags & VKI_SA_RESTART) ? True : False;
          new_action->ksa_flags |= VKI_SA_RESTART;
@@ -670,14 +912,21 @@
       if (old_action->ksa_handler == VKI_SIG_IGN ||
           old_action->ksa_handler == VKI_SIG_DFL) {
          /* No old action; we should have a NULL handler. */
-         vg_assert(our_old_handler == NULL);
+         vg_assert(our_old_handler == VG_SH_NOHANDLER);
       } else {
          /* There's a handler. */
          if (param1 != VKI_SIGKILL && param1 != VKI_SIGSTOP) {
             vg_assert(old_action->ksa_handler == &VG_(oursignalhandler));
 	    vg_assert((old_action->ksa_flags & VKI_SA_ONSTACK) != 0);
          }
-         old_action->ksa_handler = our_old_handler;
+	 /* Is the handler a fake one which the client doesn't know
+            about? */
+         if (vg_sighandler[param1] == VG_SH_FAKEHANDLER) {
+            /* Yes.  Pretend it was in a SIG_DFL state before. */
+            old_action->ksa_handler = VKI_SIG_DFL;
+         } else {
+            old_action->ksa_handler = our_old_handler;
+         }
          /* Since the client is not allowed to ask for an alternative
             sig stack, unset the bit for anything we pass back to
             it. */
@@ -758,7 +1007,7 @@
       return;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       Bool unblock_me = False;
       if (how == VKI_SIG_SETMASK) {
          if (!VG_(ksigismember)(set,i))
@@ -767,8 +1016,8 @@
          if (VG_(ksigismember)(set,i))
             unblock_me = True;
       }
-      if (unblock_me && VG_(sigpending)[i] == VG_SIGRUNNING) {
-         VG_(sigpending)[i] = VG_SIGIDLE;
+      if (unblock_me && vg_sigpending[i] == VG_SP_SIGRUNNING) {
+         vg_sigpending[i] = VG_SP_SIGIDLE;
 	 if (VG_(clo_verbosity) > 1)
             VG_(message)(Vg_UserMsg, 
                          "Warning: unblocking signal %d "
diff --git a/tests/pth_signal1.c b/tests/pth_signal1.c
new file mode 100644
index 0000000..4063412
--- /dev/null
+++ b/tests/pth_signal1.c
@@ -0,0 +1,141 @@
+/********************************************************
+ * An example source module to accompany...
+ *
+ * "Using POSIX Threads: Programming with Pthreads"
+ *     by Brad nichols, Dick Buttlar, Jackie Farrell
+ *     O'Reilly & Associates, Inc.
+ *
+ ********************************************************
+ * sig.c
+ *
+ * Simple example of pthreads and signals.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#include <pthread.h>
+
+#define MAX_NUM_THREADS  10
+
+
+void *catch_usr1(void *p)
+{
+  int signo=SIGUSR1;
+  /* struct sigaction action; */
+  int caught;
+  sigset_t  sigs_to_catch;
+
+  /* Identify our thread */
+  printf("\ncatchit() signal %d processing running as thread 0x%x \n", 
+	 signo, (int)pthread_self());
+  printf("Someone please send pid %d a SIGUSR1\n", getpid());
+
+  /*
+   * We inherited a thread sigmask with all the signals 
+   * blocked.  So, we can wait on whatever signals we're
+   * interested in and (as long as no other thread waits
+   * for them) we'll be sure return from sigwait() to
+   * handle it.
+   */ 
+
+  /* set this thread's signal mask to block out all other signals */
+  sigemptyset(&sigs_to_catch);
+  sigaddset(&sigs_to_catch, signo);
+
+  sigwait(&sigs_to_catch, &caught);
+
+  printf("\ncatchit() signal %d processing thread caught signal %d\n", 
+         signo, caught);
+
+  return(NULL);
+}
+
+void bugcatcher(int sig) 
+{
+  printf("The BUGCATCHER caught signal %d in thread 0x%x\n", 
+	 sig, (int)pthread_self());
+  pthread_exit(0);
+}
+
+void *cause_sig_sync(void *p)
+{
+  int i, id;
+  sigset_t sigs_to_catch;
+
+  /* Identify our thread */
+  printf("cause_sig_sync() running in thread 0x%x\n", (int)pthread_self()); 
+
+  /* set this thread's signal mask to block out all other signals */
+  sigemptyset(&sigs_to_catch);
+  sigaddset(&sigs_to_catch, SIGSEGV);
+  sigaddset(&sigs_to_catch, SIGBUS);
+  pthread_sigmask(SIG_UNBLOCK, &sigs_to_catch, NULL);
+
+  /* Loop simulating useful processing in this thread */
+  for(i=1;i==i;i++) {
+    printf("printing count: %4d\r",i);
+    if (i%100 == 0) {
+      id = *(int *)p; /* Guaranteed bad address */
+    }
+  }
+
+  return(NULL); 
+}
+
+extern int
+main(void)
+{
+  int       i;
+  pthread_t threads[MAX_NUM_THREADS];
+  int       num_threads = 0;
+  sigset_t  sigs_to_block;
+  struct    sigaction action;
+
+
+  /* Identify our thread */
+  printf("main() running in thread 0x%x\n", (int)pthread_self()); 
+
+  /* 
+   * Set this thread's signal mask to block out all other signals
+   * Other threads will inherit the mask
+   */
+  sigfillset(&sigs_to_block);
+  pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL);
+
+  /* Set signal handler for catching SIGSEGV and SIGBUS */
+  action.sa_handler=bugcatcher;
+  sigaction(SIGSEGV, &action, NULL);
+  sigaction(SIGBUS, &action, NULL);
+
+  /* spawn the threads */
+   
+  /* Make sure we can catch synchronous signals as exceptions */
+  pthread_create(&threads[num_threads++],
+		 NULL,
+                 cause_sig_sync,
+		 NULL);
+  
+  /* Rather than install the action/handler for the process,
+     we create a thread to wait for the signal */
+  pthread_create(&threads[num_threads++],
+		 NULL,
+                 catch_usr1,
+		 NULL);
+
+  printf("main()\t\t\t\t%d threads created\n",num_threads);
+  
+  /* wait until all threads have finished */
+  for (i = 0; i < num_threads; i++) {
+    pthread_join(threads[i], NULL);
+    printf("main()\t\tjoined to thread %d \n", i);
+  }
+  
+  printf("main()\t\tall %d threads have finished. \n", num_threads);
+
+  return 0;
+}
+
diff --git a/tests/signal2.c b/tests/signal2.c
index 5797537..f04b1b4 100644
--- a/tests/signal2.c
+++ b/tests/signal2.c
@@ -13,7 +13,7 @@
    printf ( "installing sig handler\n" );
    signal(SIGSEGV, sig_hdlr);
    printf ( "doing bad thing\n" );
-   * (int*) 0 = 0;
+   * (int*) 65536 = 0;
    printf ( "exited normally ?!\n" );
    return 0;
 }
diff --git a/vg_errcontext.c b/vg_errcontext.c
index aabbe43..6175339 100644
--- a/vg_errcontext.c
+++ b/vg_errcontext.c
@@ -288,7 +288,7 @@
       case Stack: 
          VG_(message)(Vg_UserMsg, 
                       "   Address 0x%x is on thread %d's stack", 
-                      ai->stack_tid, a);
+                      a, ai->stack_tid);
          break;
       case Unknown:
          if (ai->maybe_gcc) {
diff --git a/vg_include.h b/vg_include.h
index 765d719..d1cfca0 100644
--- a/vg_include.h
+++ b/vg_include.h
@@ -433,6 +433,8 @@
 #define VG_USERREQ__PTHREAD_SETSPECIFIC     0x300F
 #define VG_USERREQ__PTHREAD_GETSPECIFIC     0x3010
 #define VG_USERREQ__READ_MILLISECOND_TIMER  0x3011
+#define VG_USERREQ__PTHREAD_SIGMASK         0x3012
+#define VG_USERREQ__SIGWAIT                 0x3013
 
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
@@ -478,6 +480,7 @@
       VgTs_WaitFD,     /* waiting for I/O completion on a fd */
       VgTs_WaitMX,     /* waiting on a mutex */
       VgTs_WaitCV,     /* waiting on a condition variable */
+      VgTs_WaitSIG,    /* waiting due to sigwait() */
       VgTs_Sleeping    /* sleeping for a while */
    }
    ThreadStatus;
@@ -530,6 +533,19 @@
       /* thread-specific data */
       void* specifics[VG_N_THREAD_KEYS];
 
+      /* This thread's blocked-signals mask.  Semantics is that for a
+         signal to be delivered to this thread, the signal must not be
+         blocked by either the process-wide signal mask nor by this
+         one.  So, if this thread is prepared to handle any signal that
+         the process as a whole is prepared to handle, this mask should
+         be made empty -- and that it is its default, starting
+         state. */
+      vki_ksigset_t sig_mask;
+
+      /* When not VgTs_WaitSIG, has no meaning.  When VgTs_WaitSIG,
+         is the set of signals for which we are sigwait()ing. */
+      vki_ksigset_t sigs_waited_for;
+
       /* Stacks.  When a thread slot is freed, we don't deallocate its
          stack; we just leave it lying around for the next use of the
          slot.  If the next use of the slot requires a larger stack,
@@ -581,6 +597,9 @@
    ThreadState;
 
 
+/* Trivial range check on tid. */
+extern Bool VG_(is_valid_tid) ( ThreadId tid );
+
 /* Copy the specified thread's state into VG_(baseBlock) in
    preparation for running it. */
 extern void VG_(load_thread_state)( ThreadId );
@@ -591,6 +610,7 @@
 
 /* Get the thread state block for the specified thread. */
 extern ThreadState* VG_(get_thread_state)( ThreadId );
+extern ThreadState* VG_(get_thread_state_UNCHECKED)( ThreadId );
 
 /* And for the currently running one, if valid. */
 extern ThreadState* VG_(get_current_thread_state) ( void );
@@ -663,9 +683,10 @@
 
 extern void VG_(sigstartup_actions) ( void );
 
-extern Bool VG_(deliver_signals) ( ThreadId );
+extern Bool VG_(deliver_signals) ( void );
 extern void VG_(unblock_host_signal) ( Int sigNo );
-
+extern void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid );
+extern void VG_(update_sigstate_following_WaitSIG_change) ( void );
 
 /* Fake system calls for signal handling. */
 extern void VG_(do__NR_sigaction)     ( ThreadId tid );
@@ -797,9 +818,10 @@
    definitions, which are different in places from those that glibc
    defines.  Since we're operating right at the kernel interface,
    glibc's view of the world is entirely irrelevant. */
-extern Int VG_(ksigfillset)( vki_ksigset_t* set );
-extern Int VG_(ksigemptyset)( vki_ksigset_t* set );
-extern Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
+extern Int  VG_(ksigfillset)( vki_ksigset_t* set );
+extern Int  VG_(ksigemptyset)( vki_ksigset_t* set );
+extern Bool VG_(kisemptysigset)( vki_ksigset_t* set );
+extern Int  VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
 
 extern Int VG_(ksigprocmask)( Int how, const vki_ksigset_t* set, 
                                        vki_ksigset_t* oldset );
@@ -807,6 +829,11 @@
                              const vki_ksigaction* act,  
                              vki_ksigaction* oldact );
 extern Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum );
+extern void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+extern void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+
 
 extern Int VG_(ksignal)(Int signum, void (*sighandler)(Int));
 
diff --git a/vg_kerneliface.h b/vg_kerneliface.h
index a7690dd..b42ab44 100644
--- a/vg_kerneliface.h
+++ b/vg_kerneliface.h
@@ -71,6 +71,7 @@
    }
    vki_ksigset_t;
 
+
 typedef
    struct {
       void*         ksa_handler;
@@ -89,6 +90,8 @@
    vki_kstack_t;
 
 
+
+
 #define VKI_SIG_BLOCK          0    /* for blocking signals */
 #define VKI_SIG_UNBLOCK        1    /* for unblocking signals */
 #define VKI_SIG_SETMASK        2    /* for setting the signal mask */
@@ -297,14 +300,13 @@
 };
 
 
-/* To do with the ELF constructed by the kernel on a process' stack
-   just before it transfers control to the program's interpreter
-   (to use the ELF parlance).  
+/* To do with the ELF frame constructed by the kernel on a process'
+   stack just before it transfers control to the program's interpreter
+   (to use the ELF parlance).
    Constants from /usr/src/linux-2.4.9-31/include/linux/elf.h
    Logic from     /usr/src/linux-2.4.9-31/fs/binfmt_elf.c
                   and its counterpart in the 2.2.14 kernel sources 
-                  in Red Hat 6.2.
-*/
+                  in Red Hat 6.2.  */
 #define VKI_AT_CLKTCK 17    /* frequency at which times() increments */
 #define VKI_AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
 #define VKI_AT_BASE   7     /* base address of interpreter */
diff --git a/vg_libpthread.c b/vg_libpthread.c
index 5e6e14c..a7dfb70 100644
--- a/vg_libpthread.c
+++ b/vg_libpthread.c
@@ -617,6 +617,61 @@
 
 
 /* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+#include <signal.h>
+
+int pthread_sigmask(int how, const sigset_t *newmask, 
+                             sigset_t *oldmask)
+{
+   int res;
+
+   /* A bit subtle, because the scheduler expects newmask and oldmask
+      to be vki_sigset_t* rather than sigset_t*, and the two are
+      different.  Fortunately the first 64 bits of a sigset_t are
+      exactly a vki_sigset_t, so we just pass the pointers through
+      unmodified.  Haaaack! 
+
+      Also mash the how value so that the SIG_ constants from glibc
+      do not have to be included into vg_scheduler.c. */
+
+   ensure_valgrind("pthread_sigmask");
+
+   switch (how) {
+      case SIG_SETMASK: how = 1; break;
+      case SIG_BLOCK:   how = 2; break;
+      case SIG_UNBLOCK: how = 3; break;
+      default:          return EINVAL;
+   }
+
+   /* Crude check */
+   if (newmask == NULL)
+      return EFAULT;
+
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_SIGMASK,
+                           how, newmask, oldmask, 0);
+
+   /* The scheduler tells us of any memory violations. */
+   return res == 0 ? 0 : EFAULT;
+}
+
+
+int sigwait ( const sigset_t* set, int* sig )
+{
+   int res;
+   ensure_valgrind("sigwait");
+   /* As with pthread_sigmask we deliberately confuse sigset_t with
+      vki_ksigset_t. */
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__SIGWAIT,
+                           set, sig, 0, 0);
+   return res;
+}
+
+
+/* ---------------------------------------------------
    THREAD-SPECIFICs
    ------------------------------------------------ */
 
@@ -872,7 +927,6 @@
    ------------------------------------------------------------------ */
 
 #include <stdlib.h>
-#include <signal.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
diff --git a/vg_libpthread_unimp.c b/vg_libpthread_unimp.c
index c592e32..4157af6 100644
--- a/vg_libpthread_unimp.c
+++ b/vg_libpthread_unimp.c
@@ -150,7 +150,7 @@
 //void pthread_setcanceltype ( void )  { unimp("pthread_setcanceltype"); }
 //void pthread_setschedparam ( void )  { unimp("pthread_setschedparam"); }
 //void pthread_setspecific ( void )  { unimp("pthread_setspecific"); }
-void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
+//void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
 //void pthread_testcancel ( void )  { unimp("pthread_testcancel"); }
 void raise ( void )  { unimp("raise"); }
 void sem_close ( void )  { unimp("sem_close"); }
@@ -159,7 +159,7 @@
 void sem_unlink ( void )  { unimp("sem_unlink"); }
 //void sigaction ( void )  { unimp("sigaction"); }
 void siglongjmp ( void )  { unimp("siglongjmp"); }
-void sigwait ( void )  { unimp("sigwait"); }
+//void sigwait ( void )  { unimp("sigwait"); }
 
 #if 0
 void pthread_create@@GLIBC_2.1 ( void )  { unimp("pthread_create@@GLIBC_2.1"); }
diff --git a/vg_mylibc.c b/vg_mylibc.c
index f42ba94..4b4c8f3 100644
--- a/vg_mylibc.c
+++ b/vg_mylibc.c
@@ -154,6 +154,15 @@
    return 0;
 }
 
+Bool VG_(kisemptysigset)( vki_ksigset_t* set )
+{
+   Int i;
+   vg_assert(set != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      if (set->ws[i] != 0x0) return False;
+   return True;
+}
+
 Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
@@ -168,9 +177,9 @@
 Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
-      return -1;
+      return 0;
    if (signum < 1 && signum > VKI_KNSIG)
-      return -1;
+      return 0;
    signum--;
    if (1 & ((set->ws[signum / VKI_KNSIG_BPW]) >> (signum % VKI_KNSIG_BPW)))
       return 1;
@@ -179,6 +188,25 @@
 }
 
 
+/* Add all signals in src to dst. */
+void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] |= src->ws[i];
+}
+
+/* Remove all signals in src from dst. */
+void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] &= ~(src->ws[i]);
+}
+
+
 /* The functions sigaction, sigprocmask, sigpending and sigsuspend
    return 0 on success and -1 on error.  
 */
diff --git a/vg_scheduler.c b/vg_scheduler.c
index 53edabb..d50fe93 100644
--- a/vg_scheduler.c
+++ b/vg_scheduler.c
@@ -30,7 +30,6 @@
 
 #include "vg_include.h"
 #include "vg_constants.h"
-
 #include "valgrind.h" /* for VG_USERREQ__MAKE_NOACCESS and
                          VG_USERREQ__DO_LEAK_CHECK */
 
@@ -156,8 +155,8 @@
    Helper functions for the scheduler.
    ------------------------------------------------------------------ */
 
-static __inline__
-Bool is_valid_tid ( ThreadId tid )
+__inline__
+Bool VG_(is_valid_tid) ( ThreadId tid )
 {
    /* tid is unsigned, hence no < 0 test. */
    if (tid == 0) return False;
@@ -216,6 +215,7 @@
          case VgTs_Sleeping:   VG_(printf)("Sleeping"); break;
          case VgTs_WaitMX:     VG_(printf)("WaitMX"); break;
          case VgTs_WaitCV:     VG_(printf)("WaitCV"); break;
+         case VgTs_WaitSIG:    VG_(printf)("WaitSIG"); break;
          default: VG_(printf)("???"); break;
       }
       VG_(printf)(", associated_mx = %p, associated_cv = %p\n", 
@@ -340,9 +340,16 @@
 }
 
 
+ThreadState* VG_(get_thread_state_UNCHECKED) ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   return & vg_threads[tid];
+}
+
+
 ThreadState* VG_(get_thread_state) ( ThreadId tid )
 {
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
    return & vg_threads[tid];
 }
@@ -461,7 +468,7 @@
 UInt run_thread_for_a_while ( ThreadId tid )
 {
    volatile UInt trc = 0;
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
    vg_assert(VG_(bbs_to_go) > 0);
 
@@ -541,6 +548,8 @@
       vg_threads[i].stack_size = 0;
       vg_threads[i].stack_base = (Addr)NULL;
       vg_threads[i].tid        = i;
+      VG_(ksigemptyset)(&vg_threads[i].sig_mask);
+      VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
    }
 
    for (i = 0; i < VG_N_WAITING_FDS; i++)
@@ -714,7 +723,7 @@
 {
    Int  i, waiters;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_WaitFD);
    vg_assert(vg_threads[tid].m_eax == __NR_read 
              || vg_threads[tid].m_eax == __NR_write);
@@ -744,7 +753,7 @@
    Char msg_buf[100];
    Bool restart_blocked_syscalls;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    restart_blocked_syscalls = VG_(signal_returns)(tid);
 
@@ -793,7 +802,7 @@
    Bool orig_fd_blockness;
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    syscall_no = vg_threads[tid].m_eax; /* syscall number */
@@ -969,7 +978,7 @@
       if (fd > fd_max) 
          fd_max = fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
       syscall_no = vg_waiting_fds[i].syscall_no;
       switch (syscall_no) {
          case __NR_read:
@@ -1075,7 +1084,7 @@
 
       fd  = vg_waiting_fds[i].fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
 
       /* The thread actually has to be waiting for the I/O event it
          requested before we can deliver the result! */
@@ -1214,7 +1223,7 @@
             even if, as a result, it has missed the unlocking of it.
             Potential deadlock.  This sounds all very strange, but the
             POSIX standard appears to require this behaviour.  */
-         sigs_delivered = VG_(deliver_signals)( 1 /*HACK*/ );
+         sigs_delivered = VG_(deliver_signals)();
 	 if (sigs_delivered)
             VG_(do_sanity_checks)( False );
 
@@ -1226,6 +1235,7 @@
             if (tid_next >= VG_N_THREADS) tid_next = 1;
             if (vg_threads[tid_next].status == VgTs_WaitFD
                 || vg_threads[tid_next].status == VgTs_Sleeping
+                || vg_threads[tid_next].status == VgTs_WaitSIG
                 || (vg_threads[tid_next].status == VgTs_WaitCV 
                     && vg_threads[tid_next].awaken_at != 0xFFFFFFFF))
                n_in_bounded_wait ++;
@@ -1257,7 +1267,7 @@
             thread becomes runnable. */
          nanosleep_for_a_while();
 	 /* pp_sched_status(); */
-	 /* VG_(printf)(".\n"); */
+	 /* VG_(printf)("."); */
       }
 
 
@@ -1524,16 +1534,37 @@
    Thread CREATION, JOINAGE and CANCELLATION.
    -------------------------------------------------------- */
 
+/* Release resources and generally clean up once a thread has finally
+   disappeared. */
+static
+void cleanup_after_thread_exited ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   vg_assert(vg_threads[tid].status == VgTs_Empty);
+   /* Mark its stack no-access */
+   if (VG_(clo_instrument) && tid != 1)
+      VGM_(make_noaccess)( vg_threads[tid].stack_base,
+                           vg_threads[tid].stack_size );
+   /* Forget about any pending signals directed specifically at this
+      thread. */
+   VG_(notify_signal_machinery_of_thread_exit)( tid );
+
+   /* Get rid of signal handlers specifically arranged for this
+      thread. */
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 static
 void do_pthread_cancel ( ThreadId  tid,
                          pthread_t tid_cancellee )
 {
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
-   if (!is_valid_tid(tid_cancellee)
+   if (!VG_(is_valid_tid)(tid_cancellee)
        || vg_threads[tid_cancellee].status == VgTs_Empty) {
       SET_EDX(tid, ESRCH);
       return;
@@ -1588,7 +1619,7 @@
 
    /* Mark it as not in use.  Leave the stack in place so the next
       user of this slot doesn't reallocate it. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
    vg_threads[tid].retval = retval;
@@ -1611,7 +1642,7 @@
          call.  TODO: free properly the slot (also below). 
       */
       jnr = vg_threads[tid].joiner;
-      vg_assert(is_valid_tid(jnr));
+      vg_assert(VG_(is_valid_tid)(jnr));
       vg_assert(vg_threads[jnr].status == VgTs_WaitJoinee);
       jnr_args = (UInt*)vg_threads[jnr].m_eax;
       jnr_thread_return = (void**)(jnr_args[2]);
@@ -1620,9 +1651,7 @@
       SET_EDX(jnr, 0); /* success */
       vg_threads[jnr].status = VgTs_Runnable;
       vg_threads[tid].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && tid != 0)
-         VGM_(make_noaccess)( vg_threads[tid].stack_base,
-                              vg_threads[tid].stack_size );
+      cleanup_after_thread_exited ( tid );
       if (VG_(clo_trace_sched)) {
          VG_(sprintf)(msg_buf, 
             "root fn returns, to find a waiting pthread_join(%d)", tid);
@@ -1645,7 +1674,7 @@
 
    /* jee, the joinee, is the thread specified as an arg in thread
       tid's call to pthread_join.  So tid is the join-er. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    if (jee == tid) {
@@ -1687,9 +1716,7 @@
       }
       vg_threads[tid].status = VgTs_Runnable;
       vg_threads[jee].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && jee != 0)
-         VGM_(make_noaccess)( vg_threads[jee].stack_base,
-                              vg_threads[jee].stack_size );
+      cleanup_after_thread_exited ( jee );
       if (VG_(clo_trace_sched)) {
 	 VG_(sprintf)(msg_buf,
 		      "someone called pthread_join() on me; bye!");
@@ -1736,7 +1763,7 @@
 
    /* If we've created the main thread's tid, we're in deep trouble :) */
    vg_assert(tid != 1);
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    /* Copy the parent's CPU state into the child's, in a roundabout
       way (via baseBlock). */
@@ -1807,6 +1834,10 @@
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
       vg_threads[tid].specifics[i] = NULL;
 
+   /* We inherit our parent's signal mask. (?!) */
+   vg_threads[tid].sig_mask = vg_threads[parent_tid].sig_mask;
+   VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
+
    /* return zero */
    SET_EDX(parent_tid, 0); /* success */
 }
@@ -1920,7 +1951,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    /* POSIX doesn't mandate this, but for sanity ... */
@@ -1951,7 +1982,7 @@
 
    if (mutex->__m_count > 0) {
 
-      vg_assert(is_valid_tid((ThreadId)mutex->__m_owner));
+      vg_assert(VG_(is_valid_tid)((ThreadId)mutex->__m_owner));
 
       /* Someone has it already. */
       if ((ThreadId)mutex->__m_owner == tid) {
@@ -2018,7 +2049,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL) {
@@ -2109,7 +2140,7 @@
    pthread_mutex_t* mx;
    pthread_cond_t*  cv;
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_WaitCV
              && vg_threads[tid].awaken_at != 0xFFFFFFFF);
    mx = vg_threads[tid].associated_mx;
@@ -2238,7 +2269,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL || cond == NULL) {
@@ -2306,7 +2337,7 @@
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (cond == NULL) {
@@ -2352,7 +2383,7 @@
    }
 
    vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey));
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
@@ -2388,7 +2419,7 @@
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
    
    if (!is_valid_key(key)) {
@@ -2420,7 +2451,7 @@
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2444,7 +2475,7 @@
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2457,6 +2488,85 @@
 }
 
 
+/* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+/* See comment in vg_libthread.c:pthread_sigmask() regarding
+   deliberate confusion of types sigset_t and vki_sigset_t.  Also re
+   meaning of the mashed_how value.  Return 0 for OK and 1 for some
+   kind of addressing error, which the vg_libpthread.c routine turns
+   into return values 0 and EFAULT respectively. */
+static
+void do_pthread_sigmask ( ThreadId tid,
+                          Int mashed_how,
+                          vki_ksigset_t* newmask, 
+                          vki_ksigset_t* oldmask )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_pthread_level) >= 1) {
+      VG_(sprintf)(msg_buf, 
+         "pthread_sigmask          m_how %d, newmask %p, oldmask %p",
+         mashed_how, newmask, oldmask );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   if (VG_(clo_instrument)) {
+      /* TODO check newmask/oldmask are addressible/defined */
+   }
+
+   if (oldmask != NULL) {
+      *oldmask = vg_threads[tid].sig_mask;
+      if (VG_(clo_instrument)) {
+         VGM_(make_readable)( (Addr)oldmask, sizeof(vki_ksigset_t) );
+      }
+   }
+
+   switch (mashed_how) {
+      case 1: /* SIG_SETMASK */
+         vg_threads[tid].sig_mask = *newmask;
+         break;
+      case 2: /* SIG_BLOCK */
+         VG_(ksigaddset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      case 3: /* SIG_UNBLOCK */
+         VG_(ksigdelset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      default: 
+        VG_(panic)("do_pthread_sigmask: invalid mashed_how");
+        /*NOTREACHED*/
+        break;
+   }
+
+   SET_EDX(tid, 0);
+}
+
+
+static
+void do_sigwait ( ThreadId tid,
+                  vki_ksigset_t* set, 
+                  Int* sig )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_signals) || VG_(clo_trace_sched)) {
+      VG_(sprintf)(msg_buf, 
+         "suspend due to sigwait(): set %p, sig %p",
+         set, sig );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   vg_threads[tid].sigs_waited_for = *set;
+   vg_threads[tid].status = VgTs_WaitSIG;
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 /* ---------------------------------------------------------------------
    Handle non-trivial client requests.
    ------------------------------------------------------------------ */
@@ -2537,6 +2647,19 @@
 				  (void*)(arg[2]) );
  	 break;
 
+      case VG_USERREQ__PTHREAD_SIGMASK:
+         do_pthread_sigmask ( tid,
+                              arg[1],
+                              (vki_ksigset_t*)(arg[2]),
+                              (vki_ksigset_t*)(arg[3]) );
+	 break;
+
+      case VG_USERREQ__SIGWAIT:
+         do_sigwait ( tid,
+                      (vki_ksigset_t*)(arg[1]),
+                      (Int*)(arg[2]) );
+	 break;
+
       case VG_USERREQ__MAKE_NOACCESS:
       case VG_USERREQ__MAKE_WRITABLE:
       case VG_USERREQ__MAKE_READABLE:
@@ -2595,7 +2718,7 @@
          vg_assert(cv == NULL);
          /* 1 */ vg_assert(mx != NULL);
 	 /* 2 */ vg_assert(mx->__m_count > 0);
-         /* 3 */ vg_assert(is_valid_tid((ThreadId)mx->__m_owner));
+         /* 3 */ vg_assert(VG_(is_valid_tid)((ThreadId)mx->__m_owner));
          /* 4 */ vg_assert(i != (ThreadId)mx->__m_owner); 
       } else 
       if (vg_threads[i].status == VgTs_WaitCV) {
@@ -2626,6 +2749,15 @@
                "VG_PTHREAD_STACK_SIZE in vg_include.h and recompile.");
             VG_(exit)(1);
 	 }
+
+         if (vg_threads[i].status == VgTs_WaitSIG) {
+            vg_assert( ! VG_(kisemptysigset)(
+                            & vg_threads[i].sigs_waited_for) );
+	 } else {
+            vg_assert( VG_(kisemptysigset)(
+                          & vg_threads[i].sigs_waited_for) );
+	 }
+
       }
    }
 
diff --git a/vg_signals.c b/vg_signals.c
index cbc72d3..3edff21 100644
--- a/vg_signals.c
+++ b/vg_signals.c
@@ -39,33 +39,83 @@
    Signal state for this process.
    ------------------------------------------------------------------ */
 
-/* For each signal, the current action.  Is NULL if the client hasn't
-   asked to handle the signal.  Consequently, we expect never to
-   receive a signal for which the corresponding handler is NULL. */
-void* VG_(sighandler)[VKI_KNSIG];
+/* 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. 
+ */
+
+/* For each signal, the current action.  Either:
+
+   -- VG_SH_NOHANDLER if the client hasn't asked to handle the signal, 
+      and we havent surreptitiously installed any handler ourselves.
+
+   -- VG_SH_FAKEHANDLER if the client hasn't asked to handle the signal
+      directly, but has so indirectly via a sigwait() request.  In this
+      case we may need to install our own handler to catch signals which
+      the sigwait-mask for some thread will accept, but for which the
+      client hasn't actually installed a handler.  These "fake" handlers
+      are invisible to the client, so we need to be able to distinguish
+      this case so that we can fake a suitable response if the client
+      should enquire about the state of this signal using sigaction.
+
+   -- Otherwise, the client has installed a signal handler, and this
+      is the pointer to it.
+
+   Invariant: we never expect to receive a signal for which the 
+   vg_sighandler[] entry is VG_SH_NOHANDLER.  If it is VG_SH_FAKEHANDLER
+   we know that we should look for a thread in VgTs_WaitSIG state to
+   release.  Otherwise, we find a thread capable of handling this
+   signal and run the specified handler on it.
+*/
+#define VG_SH_NOHANDLER    ((void*)0)
+#define VG_SH_FAKEHANDLER  ((void*)1)
+
+void* vg_sighandler[1+VKI_KNSIG];
 
 
 /* For each signal, either:
-   -- VG_SIGIDLE if not pending and not running
-   -- Handler address if pending
-   -- VG_SIGRUNNING if the handler is running and hasn't (returned or 
+   -- VG_SP_SIGIDLE if not pending and not running
+   -- Handler address if pending AND real handler
+   -- VG_SH_FAKEHANDLER if pending for sigwait
+   -- VG_SP_SIGRUNNING if the handler is running and hasn't (returned or 
       unblocked the signal using sigprocmask following a longjmp out 
       of the handler).
  */
-#define VG_SIGIDLE    ((void*)0)
-#define VG_SIGRUNNING ((void*)1)
+#define VG_SP_SIGIDLE    ((void*)0)
+#define VG_SP_SIGRUNNING ((void*)2)
 
-void* VG_(sigpending)[VKI_KNSIG];
+static
+void* vg_sigpending[1+VKI_KNSIG];
 
 
-/* For each signal that we have a handler for (ie, for those for which
-   the VG_(sighandler) entry is non-NULL), record whether or not the
-   client asked for syscalls to be restartable (SA_RESTART) if
-   interrupted by this signal.  We need to consult this when a signal
-   returns, if it should happen that the signal which we delivered has
-   interrupted a system call. */
+/* For each signal, the thread id to which the signal should be
+   delivered.  This is only meaningful if the corresponding
+   vg_sigpending entry actually points to a handler, ie, the signal
+   is pending.
+
+   In this case, the value VG_INVALID_THREADID indicates the signal is
+   not directed at a specific thread and so should be delivered to any
+   thread whose signal mask (ThreadState.sig_mask) field allows it.
+
+   Any other value indicates that the signal should be delivered only
+   to that specific thread, as some point in time when the thread has
+   not blocked the signal.  It remains pending until then. */
+static
+ThreadId vg_sig_threadid[1+VKI_KNSIG];
+
+
+/* For each signal that the client installed a handler for (ie, for
+   those for which the vg_sighandler entry is non-VG_SH_NOHANDLER and
+   non-VG_SH_FAKEHANDLER), record whether or not the client asked for
+   syscalls to be restartable (SA_RESTART) if interrupted by this
+   signal.  We need to consult this when a signal returns, if it
+   should happen that the signal which we delivered has interrupted a
+   system call. */
 static 
-Bool vg_sig_sarestart[VKI_KNSIG];
+Bool vg_sig_sarestart[1+VKI_KNSIG];
 
 
 /* ---------------------------------------------------------------------
@@ -179,7 +229,7 @@
 
    /* Set the thread so it will next run the handler. */
    tst->m_esp  = esp;
-   tst->m_eip  = (Addr)VG_(sigpending)[sigNo];
+   tst->m_eip  = (Addr)vg_sigpending[sigNo];
    /* This thread needs to be marked runnable, but we leave that the
       caller to do. */
 
@@ -218,7 +268,8 @@
    vg_assert(frame->magicPI == 0x31415927);
    vg_assert(frame->magicE  == 0x27182818);
    if (VG_(clo_trace_signals))
-      VG_(message)(Vg_DebugMsg, "vg_pop_signal_frame: valid magic");
+      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++)
@@ -276,7 +327,7 @@
    /* You would have thought that the following assertion made sense
       here:
 
-         vg_assert(vg_sigpending[sigNo] == VG_SIGRUNNING);
+         vg_assert(vg_sigpending[sigNo] == VG_SP_SIGRUNNING);
 
       Alas, you would be wrong.  If a sigprocmask has been intercepted
       and it unblocks this signal, then vg_sigpending[sigNo] will
@@ -291,8 +342,8 @@
       Ho Hum.  This seems like a race condition which surely isn't
       handled correctly.  */
 
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
-   VG_(sigpending)[sigNo] = VG_SIGIDLE;
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
+   vg_sigpending[sigNo] = VG_SP_SIGIDLE;
 
    /* Unlock and return. */
    VG_(restore_host_signals)( &saved_procmask );
@@ -306,12 +357,14 @@
 
 /* Deliver all pending signals, by building stack frames for their
    handlers.  Return True if any signals were delivered. */
-Bool VG_(deliver_signals) ( ThreadId tid )
+Bool VG_(deliver_signals) ( void )
 {
    vki_ksigset_t  saved_procmask;
    Int            sigNo;
    Bool           found;
- 
+   ThreadState*   tst;
+   ThreadId       tid;
+
    /* A cheap check.  We don't need to have exclusive access
       to the queue, because in the worst case, vg_oursignalhandler
       will add signals, causing us to return, thinking there
@@ -319,9 +372,10 @@
       A subsequent call here will handle the signal(s) we missed.
    */
    found = False;
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++)
-      if (VG_(sigpending)[sigNo] != VG_SIGIDLE &&
-          VG_(sigpending)[sigNo] != VG_SIGRUNNING) found = True;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++)
+      if (vg_sigpending[sigNo] != VG_SP_SIGIDLE 
+          && vg_sigpending[sigNo] != VG_SP_SIGRUNNING) 
+         found = True;
 
    if (!found) return False;
 
@@ -332,22 +386,98 @@
    VG_(block_all_host_signals)( &saved_procmask );
 
    /* Look for signals to deliver ... */
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++) {
-      if (VG_(sigpending)[sigNo] == VG_SIGIDLE ||
-          VG_(sigpending)[sigNo] == VG_SIGRUNNING) continue;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING) 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_(get_thread_state_UNCHECKED)(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_(get_thread_state)(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));
+         }
+	 tst->m_edx = 0;
+         tst->sh_edx = VGM_WORD_VALID;
+         tst->status = VgTs_Runnable;
+         VG_(update_sigstate_following_WaitSIG_change)();
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         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 so the
+         handler better be valid. */
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] != VG_SH_FAKEHANDLER);
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGRUNNING);
+
+      tid = vg_sig_threadid[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_(get_thread_state_UNCHECKED)(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_sigpending[sigNo] = VG_SP_SIGIDLE;
+            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_(get_thread_state_UNCHECKED)(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", sigNo );
+         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_assert(VG_(get_thread_state)(tid)->status != VgTs_Empty);
       vg_push_signal_frame ( tid, sigNo );
       VG_(get_thread_state)(tid)->status = VgTs_Runnable;
       
       /* Signify that the signal has been delivered. */
-      VG_(sigpending)[sigNo] = VG_SIGRUNNING;
+      vg_sigpending[sigNo] = VG_SP_SIGRUNNING;
    }
 
    /* Unlock and return. */
@@ -356,12 +486,36 @@
 }
 
 
+/* A thread is about to exit.  Forget about any signals which are
+   still pending for it. */
+void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid )
+{
+   Int sigNo;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING)
+         continue;
+      if (vg_sig_threadid[sigNo] == tid) {
+         /* sigNo is pending for tid, which is just about to disappear.
+            So forget about the pending signal. */
+         vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         if (VG_(clo_trace_signals)) 
+            VG_(message)(Vg_DebugMsg, 
+                "discarding pending signal %d due to thread %d exiting",
+                sigNo, tid );
+      }   
+   }
+}
+
+
 /* 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 )
+static 
+void VG_(oursignalhandler) ( Int sigNo )
 {
    Int           dummy_local;
    vki_ksigset_t saved_procmask;
@@ -381,7 +535,7 @@
       VG_(start_msg)(Vg_DebugMsg);
       VG_(add_to_msg)("signal %d arrived ... ", sigNo );
    }
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
 
    /* Sanity check.  Ensure we're really running on the signal stack
       we asked for. */
@@ -407,7 +561,7 @@
 
    VG_(block_all_host_signals)( &saved_procmask );
 
-   if (VG_(sighandler)[sigNo] == NULL) {
+   if (vg_sighandler[sigNo] == VG_SH_NOHANDLER) {
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("unexpected!");
          VG_(end_msg)();
@@ -418,7 +572,7 @@
    }
 
    /* Decide what to do with it. */
-   if (VG_(sigpending)[sigNo] == VG_SIGRUNNING) {
+   if (vg_sigpending[sigNo] == VG_SP_SIGRUNNING) {
        /* Already running; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already running; discarded" );
@@ -426,8 +580,8 @@
       }
    }
    else
-   if (VG_(sigpending)[sigNo] != VG_SIGRUNNING && 
-       VG_(sigpending)[sigNo] != VG_SIGIDLE) {
+   if (vg_sigpending[sigNo] != VG_SP_SIGRUNNING 
+       && vg_sigpending[sigNo] != VG_SP_SIGIDLE) {
       /* Not running and not idle == pending; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already pending; discarded" );
@@ -436,9 +590,11 @@
    } 
    else {
       /* Ok, we'd better deliver it to the client. */
-      vg_assert(VG_(sigpending)[sigNo] == VG_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] == VG_SP_SIGIDLE);
       /* Queue it up for delivery at some point in the future. */
-      VG_(sigpending)[sigNo] = VG_(sighandler)[sigNo];
+      vg_assert(vg_sighandler[sigNo] != VG_SH_NOHANDLER);
+      vg_sigpending[sigNo] = vg_sighandler[sigNo];
+      vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("queued" );
          VG_(end_msg)();
@@ -481,7 +637,7 @@
    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++)
+   for (i = 1; i <= VKI_KNSIG; i++)
       if (VG_(ksigismember(&(sa->ksa_mask),i)))
          VG_(printf)("%d ", i);
    VG_(printf)("}\n");
@@ -520,13 +676,14 @@
    }
 
    /* Set initial state for the signal simulation. */
-   for (i = 1; i < VKI_KNSIG; i++) {
-      VG_(sighandler)[i] = NULL;
-      VG_(sigpending)[i] = NULL;
+   for (i = 1; i <= VKI_KNSIG; i++) {
+      vg_sighandler[i] = VG_SH_NOHANDLER;
+      vg_sigpending[i] = VG_SP_SIGIDLE;
       vg_sig_sarestart[i] = True; /* An easy default */
+      vg_sig_threadid[i] = VG_INVALID_THREADID;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
 
       /* Get the old host action */
       ret = VG_(ksigaction)(i, NULL, &sa);
@@ -534,7 +691,8 @@
 
       /* If there's already a handler set, record it, then route the
          signal through to our handler. */
-      if (sa.ksa_handler != VKI_SIG_IGN && sa.ksa_handler != VKI_SIG_DFL) {
+      if (sa.ksa_handler != VKI_SIG_IGN 
+          && sa.ksa_handler != VKI_SIG_DFL) {
          if (VG_(clo_trace_signals))
             VG_(printf)("snaffling handler 0x%x for signal %d\n", 
                         (Addr)(sa.ksa_handler), i );
@@ -542,7 +700,7 @@
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
 
-         VG_(sighandler)[i] = sa.ksa_handler;
+         vg_sighandler[i] = sa.ksa_handler;
          sa.ksa_handler = &VG_(oursignalhandler);
 	 /* Save the restart status, then set it to restartable. */
 	 vg_sig_sarestart[i] 
@@ -585,12 +743,13 @@
    /* copy the sim signal actions to the real ones. */
    /* Hmm, this isn't accurate.  Doesn't properly restore the
       SA_RESTART flag nor SA_ONSTACK. */
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       if (i == VKI_SIGKILL || i == VKI_SIGSTOP) continue;
-      if (VG_(sighandler)[i] == NULL) continue;
+      if (vg_sighandler[i] == VG_SH_NOHANDLER 
+          || vg_sighandler[i] == VG_SH_FAKEHANDLER) continue;
       ret = VG_(ksigaction)(i, NULL, &sa);
       vg_assert(ret == 0);
-      sa.ksa_handler = VG_(sighandler)[i];
+      sa.ksa_handler = vg_sighandler[i];
       ret = VG_(ksigaction)(i, &sa, NULL);      
    }
 
@@ -598,6 +757,89 @@
 }
 
 
+void VG_(update_sigstate_following_WaitSIG_change) ( void )
+{
+   ThreadId      tid;
+   Int           sig;
+   vki_ksigset_t global_waitsigs;
+   ThreadState*  tst;
+
+   VG_(ksigemptyset)( &global_waitsigs );
+
+   /* Calculate the new set of signals which are being sigwait()d for
+      by at least one thread. */
+   for (tid = 1; tid < VG_N_THREADS; tid++) {
+      tst = VG_(get_thread_state_UNCHECKED)(tid);
+      if (tst->status != VgTs_WaitSIG)
+         continue;
+      vg_assert(! VG_(kisemptysigset)(
+                     & tst->sigs_waited_for ));
+      VG_(ksigaddset_from_set)( & global_waitsigs, 
+                                & tst->sigs_waited_for );
+   }
+
+   /* Now adjust vg_sighandler accordingly.
+
+      For each signal s: (lapses into pseudo-Haskell ...)
+
+      if s `elem` global_waitsigs[s]
+       -- at least one thread is sigwait()ing for s.  That means that at 
+          least _some_ kind of handler is needed.
+       case vg_sighandler[s] of
+          VG_SH_NOHANDLER -> install our own handler and set waitsigs[s] 
+             to VG_SH_FAKEHANDLER 
+          VG_SH_FAKEHANDLER -> there's already a handler.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+
+      if s `notElem` global_waitsigs[s]
+       -- we're not sigwait()ing for s (any longer).  
+       case vg_sighandler[s] of
+          VG_SH_FAKEHANDLER -> there is a handler installed, but ONLY for 
+             the purposes of handling sigwait().  So set it back to 
+             VG_SH_NOHANDLER and tell the kernel that we want to do the 
+             default action for s from now on, ie, we wish to deregister 
+             OUR handle.
+          VG_SH_NOHANDLER -> there was no handler anyway.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+ 
+   */
+
+   for (sig = 1; sig <= VKI_KNSIG; sig++) {
+      if (VG_(ksigismember)( & global_waitsigs, sig )) {
+         if (vg_sighandler[sig] == VG_SH_NOHANDLER
+             /* && existing kernel handler is SIG_DFL */) {
+            /* add handler */
+            /* We really only ought to do this if the existing kernel
+               handler is SIG_DFL.  That's because when queried by the
+               client's sigaction, that's what we claim it is if a fake
+               handler has been installed.  Or (perhaps better) 
+               remember the kernel's setting. 
+            */
+            VG_(ksignal)( sig, &VG_(oursignalhandler) );
+            vg_sighandler[sig] = VG_SH_FAKEHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "adding fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      } else {
+         if (vg_sighandler[sig] == VG_SH_FAKEHANDLER) {
+            /* remove handler */
+            VG_(ksignal)( sig, VKI_SIG_DFL);
+            vg_sighandler[sig] = VG_SH_NOHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "removing fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      }
+   }
+}
+
 /* ---------------------------------------------------------------------
    Handle signal-related syscalls from the simulatee.
    ------------------------------------------------------------------ */
@@ -628,7 +870,7 @@
       the call is passed to the kernel it will definitely succeed. */
 
    /* Reject out-of-range signal numbers. */
-   if (param1 < 1 || param1 >= VKI_KNSIG) goto bad_signo;
+   if (param1 < 1 || param1 > VKI_KNSIG) goto bad_signo;
 
    /* Reject attempts to set a handler (or set ignore) for SIGKILL. */
    if ( (param1 == VKI_SIGKILL || param1 == VKI_SIGSTOP)
@@ -636,14 +878,14 @@
        && new_action->ksa_handler != VKI_SIG_DFL)
       goto bad_sigkill_or_sigstop;
 
-   our_old_handler = VG_(sighandler)[param1];
+   our_old_handler = vg_sighandler[param1];
    /* VG_(printf)("old handler = 0x%x\n", our_old_handler); */
    /* If a new handler has been specified, mess with its handler. */
    if (new_action) {
       if (new_action->ksa_handler == VKI_SIG_IGN ||
           new_action->ksa_handler == VKI_SIG_DFL) {
-         VG_(sighandler)[param1] = NULL; 
-         VG_(sigpending)[param1] = NULL;
+         vg_sighandler[param1] = VG_SH_NOHANDLER;
+         vg_sigpending[param1] = VG_SP_SIGIDLE;
          /* Dangerous!  Could lose signals like this. */
       } else {
          /* VG_(printf)("new handler = 0x%x\n", new_action->ksa_handler); */
@@ -653,7 +895,7 @@
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
          new_action->ksa_flags |= VKI_SA_ONSTACK;
-         VG_(sighandler)[param1] = new_action->ksa_handler;
+         vg_sighandler[param1] = new_action->ksa_handler;
 	 vg_sig_sarestart[param1] 
             = (new_action->ksa_flags & VKI_SA_RESTART) ? True : False;
          new_action->ksa_flags |= VKI_SA_RESTART;
@@ -670,14 +912,21 @@
       if (old_action->ksa_handler == VKI_SIG_IGN ||
           old_action->ksa_handler == VKI_SIG_DFL) {
          /* No old action; we should have a NULL handler. */
-         vg_assert(our_old_handler == NULL);
+         vg_assert(our_old_handler == VG_SH_NOHANDLER);
       } else {
          /* There's a handler. */
          if (param1 != VKI_SIGKILL && param1 != VKI_SIGSTOP) {
             vg_assert(old_action->ksa_handler == &VG_(oursignalhandler));
 	    vg_assert((old_action->ksa_flags & VKI_SA_ONSTACK) != 0);
          }
-         old_action->ksa_handler = our_old_handler;
+	 /* Is the handler a fake one which the client doesn't know
+            about? */
+         if (vg_sighandler[param1] == VG_SH_FAKEHANDLER) {
+            /* Yes.  Pretend it was in a SIG_DFL state before. */
+            old_action->ksa_handler = VKI_SIG_DFL;
+         } else {
+            old_action->ksa_handler = our_old_handler;
+         }
          /* Since the client is not allowed to ask for an alternative
             sig stack, unset the bit for anything we pass back to
             it. */
@@ -758,7 +1007,7 @@
       return;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       Bool unblock_me = False;
       if (how == VKI_SIG_SETMASK) {
          if (!VG_(ksigismember)(set,i))
@@ -767,8 +1016,8 @@
          if (VG_(ksigismember)(set,i))
             unblock_me = True;
       }
-      if (unblock_me && VG_(sigpending)[i] == VG_SIGRUNNING) {
-         VG_(sigpending)[i] = VG_SIGIDLE;
+      if (unblock_me && vg_sigpending[i] == VG_SP_SIGRUNNING) {
+         vg_sigpending[i] = VG_SP_SIGIDLE;
 	 if (VG_(clo_verbosity) > 1)
             VG_(message)(Vg_UserMsg, 
                          "Warning: unblocking signal %d "