1999-08-13  Jim Kingdon  <http://developer.redhat.com/>

	Threads code from gdb 4.18-codefusion-990706:
	* infrun.c (signal_stop_update, signal_print_update,
	signal_pass_update): new functions.
	* inferior.h: new prototypes for above functions.
	* target.h (enum strata): add thread stratum.
	* linuxthreads.c: new file.  Support for debugging linux threads.
	* config/i386/nm-linux.h: several new prototypes for above.
	* config/i386/linux.mh: add linuxthreads.o to NATDEPFILES.

	More threads code from the same place:
	* config/i386/tm-linux.h (REALTIME_LO, REALTIME_HI): Add
	definitions.
	* target.h (enum target_signal): Add TARGET_SIGNAL_REALTIME_32.
	* target.c (signals, target_signal_from_host,
	target_signal_to_host): Add clauses for
	TARGET_SIGNAL_REALTIME_32.

	* various files: various minor changes to make the above work
	with GDB 4.18.

diff -Ncr /home/kingdon/work/gdb/gdb/config/i386/linux.mh ./gdb/config/i386/linux.mh
*** /home/kingdon/work/gdb/gdb/config/i386/linux.mh	Thu Apr 15 21:34:19 1999
--- ./gdb/config/i386/linux.mh	Fri Aug 13 00:51:14 1999
***************
*** 4,7 ****
  XDEPFILES= ser-tcp.o
  
  NAT_FILE= nm-linux.h
! NATDEPFILES= infptrace.o solib.o inftarg.o fork-child.o corelow.o core-aout.o core-regset.o i386v-nat.o i386v4-nat.o
--- 4,7 ----
  XDEPFILES= ser-tcp.o
  
  NAT_FILE= nm-linux.h
! NATDEPFILES= infptrace.o solib.o inftarg.o fork-child.o corelow.o core-aout.o core-regset.o i386v-nat.o i386v4-nat.o linuxthreads.o
diff -Ncr /home/kingdon/work/gdb/gdb/config/i386/nm-linux.h ./gdb/config/i386/nm-linux.h
*** /home/kingdon/work/gdb/gdb/config/i386/nm-linux.h	Thu Jul  8 16:09:02 1999
--- ./gdb/config/i386/nm-linux.h	Fri Aug 13 00:49:54 1999
***************
*** 71,74 ****
--- 71,92 ----
  extern int
  i386_remove_watchpoint PARAMS ((int pid, CORE_ADDR addr, int len));
  
+ /* Support for the glibc linuxthreads package. */
+ 
+ #ifdef __STDC__
+ struct objfile;
+ #endif
+ 
+ extern void
+ linuxthreads_new_objfile PARAMS ((struct objfile *objfile));
+ #define target_new_objfile(OBJFILE) linuxthreads_new_objfile (OBJFILE)
+ 
+ extern char *
+ linuxthreads_pid_to_str PARAMS ((int pid));
+ #define target_pid_to_str(PID) linuxthreads_pid_to_str (PID)
+ 
+ extern int
+ linuxthreads_prepare_to_proceed PARAMS ((int step));
+ #define PREPARE_TO_PROCEED() linuxthreads_prepare_to_proceed (1)
+ 
  #endif /* #ifndef NM_LINUX_H */
diff -Ncr /home/kingdon/work/gdb/gdb/config/i386/tm-linux.h ./gdb/config/i386/tm-linux.h
*** /home/kingdon/work/gdb/gdb/config/i386/tm-linux.h	Tue Aug  3 16:40:28 1999
--- ./gdb/config/i386/tm-linux.h	Sat Aug 14 19:15:35 1999
***************
*** 104,107 ****
--- 104,121 ----
  
  extern CORE_ADDR i386_linux_sigtramp_saved_sp PARAMS ((struct frame_info *));
  
+ 
+ /* Some versions of Linux have real-time signal support in the C library, and
+    some don't.  We have to include this file to find out.  */
+ #include <signal.h>
+ 
+ #ifdef __SIGRTMIN
+ #define REALTIME_LO __SIGRTMIN
+ #define REALTIME_HI (__SIGRTMAX + 1)
+ #else
+ #define REALTIME_LO 32
+ #define REALTIME_HI 64
+ #endif
+ 
+ 
  #endif  /* #ifndef TM_LINUX_H */
diff -Ncr /home/kingdon/work/gdb/gdb/inferior.h ./gdb/inferior.h
*** /home/kingdon/work/gdb/gdb/inferior.h	Thu Jul  8 16:02:22 1999
--- ./gdb/inferior.h	Fri Aug 13 00:43:51 1999
***************
*** 260,265 ****
--- 260,271 ----
  
  extern int signal_pass_state PARAMS ((int));
  
+ extern int signal_stop_update PARAMS ((int, int));
+ 
+ extern int signal_print_update PARAMS ((int, int));
+ 
+ extern int signal_pass_update PARAMS ((int, int));
+ 
  /* From infcmd.c */
  
  extern void tty_command PARAMS ((char *, int));
diff -Ncr /home/kingdon/work/gdb/gdb/infrun.c ./gdb/infrun.c
*** /home/kingdon/work/gdb/gdb/infrun.c	Thu Aug 12 11:13:29 1999
--- ./gdb/infrun.c	Fri Aug 13 00:33:34 1999
***************
*** 3291,3296 ****
--- 3291,3323 ----
    return signal_program[signo];
  }
  
+ int signal_stop_update (signo, state)
+      int signo;
+      int state;
+ {
+   int ret = signal_stop[signo];
+   signal_stop[signo] = state;
+   return ret;
+ }
+ 
+ int signal_print_update (signo, state)
+      int signo;
+      int state;
+ {
+   int ret = signal_print[signo];
+   signal_print[signo] = state;
+   return ret;
+ }
+ 
+ int signal_pass_update (signo, state)
+      int signo;
+      int state;
+ {
+   int ret = signal_program[signo];
+   signal_program[signo] = state;
+   return ret;
+ }
+ 
  static void
  sig_print_header (void)
  {
diff -Ncr /home/kingdon/work/gdb/gdb/linuxthreads.c ./gdb/linuxthreads.c
*** /home/kingdon/work/gdb/gdb/linuxthreads.c	Wed Dec 31 19:00:00 1969
--- ./gdb/linuxthreads.c	Fri Aug 13 00:46:01 1999
***************
*** 0 ****
--- 1,1631 ----
+ /* Low level interface for debugging GNU/Linux threads for GDB,
+    the GNU debugger.
+    Copyright 1998, 1999 Free Software Foundation, Inc.
+ 
+ This file is part of GDB.
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+ 
+ /* This module implements the debugging interface of the linuxthreads package
+    of the glibc. This package implements a simple clone()-based implementation
+    of Posix threads for Linux. To use this module, be sure that you have at
+    least the version of the linuxthreads package that holds the support of
+    GDB (currently 0.8 included in the glibc-2.0.7).
+ 
+    Right now, the linuxthreads package does not care of priority scheduling,
+    so, neither this module does; In particular, the threads are resumed
+    in any order, which could lead to different scheduling than the one
+    happening when GDB does not control the execution.
+ 
+    The latest point is that ptrace(PT_ATTACH, ...) is intrusive in Linux:
+    When a process is attached, then the attaching process becomes the current
+    parent of the attached process, and the old parent has lost this child.
+    If the old parent does a wait[...](), then this child is no longer
+    considered by the kernel as a child of the old parent, thus leading to
+    results of the call different when the child is attached and when it's not.
+ 
+    A fix has been submitted to the Linux community to solve this problem,
+    which consequences are not visible to the application itself, but on the
+    process which may wait() for the completion of the application (mostly,
+    it may consider that the application no longer exists (errno == ECHILD),
+    although it does, and thus being unable to get the exit status and resource
+    usage of the child. If by chance, it is able to wait() for the application
+    after it has died (by receiving first a SIGCHILD, and then doing a wait(),
+    then the exit status and resource usage may be wrong, because the
+    linuxthreads package heavily relies on wait() synchronization to keep
+    them correct.  */
+ 
+ #include <sys/types.h> /* for pid_t */
+ #include <sys/ptrace.h> /* for PT_* flags */
+ #include <sys/wait.h> /* for WUNTRACED and __WCLONE flags */
+ #include <signal.h> /* for struct sigaction and NSIG */
+ #include <sys/utsname.h>
+ 
+ #include "defs.h"
+ #include "target.h"
+ #include "inferior.h"
+ #include "gdbcore.h"
+ #include "gdbthread.h"
+ #include "wait.h"
+ #include "gdbcmd.h"
+ #include "breakpoint.h"
+ 
+ #ifndef PT_ATTACH
+ #define PT_ATTACH	PTRACE_ATTACH
+ #endif
+ #ifndef PT_KILL
+ #define PT_KILL		PTRACE_KILL
+ #endif
+ #ifndef PT_READ_U
+ #define PT_READ_U	PTRACE_PEEKUSR
+ #endif
+ 
+ #ifdef NSIG
+ #define LINUXTHREAD_NSIG NSIG
+ #else
+ #ifdef _NSIG
+ #define LINUXTHREAD_NSIG _NSIG
+ #endif
+ #endif
+ 
+ extern int child_suppress_run;		/* make inftarg.c non-runnable */
+ struct target_ops linuxthreads_ops;	/* Forward declaration */
+ extern struct target_ops child_ops;	/* target vector for inftarg.c */
+ 
+ static CORE_ADDR linuxthreads_handles;	/* array of linuxthreads handles */
+ static CORE_ADDR linuxthreads_manager;	/* pid of linuxthreads manager thread */
+ static CORE_ADDR linuxthreads_initial;	/* pid of linuxthreads initial thread */
+ static CORE_ADDR linuxthreads_debug;	/* linuxthreads internal debug flag */
+ static CORE_ADDR linuxthreads_num;	/* number of valid handle entries */
+ 
+ static int linuxthreads_max;		/* Maximum number of linuxthreads.
+ 					   Zero if this executable doesn't use
+ 					   threads, or wasn't linked with a
+ 					   debugger-friendly version of the
+ 					   linuxthreads library.  */
+ 
+ static int linuxthreads_sizeof_handle;	/* size of a linuxthreads handle */
+ static int linuxthreads_offset_descr;	/* h_descr offset of the linuxthreads
+ 					   handle */
+ static int linuxthreads_offset_pid;	/* p_pid offset of the linuxthreads
+ 					   descr */
+ 
+ static int linuxthreads_manager_pid;	/* manager pid */
+ static int linuxthreads_initial_pid;	/* initial pid */
+ 
+ /* These variables form a bag of threads with interesting status.  If
+    wait_thread (PID) finds that PID stopped for some interesting
+    reason (i.e. anything other than stopped with SIGSTOP), then it
+    records its status in this queue.  linuxthreads_wait and
+    linuxthreads_find_trap extract processes from here.  */
+ static int *linuxthreads_wait_pid;	/* wait array of pid */
+ static int *linuxthreads_wait_status;	/* wait array of status */
+ static int linuxthreads_wait_last;	/* index of last valid elt in
+ 					   linuxthreads_wait_{pid,status} */
+ 
+ static sigset_t linuxthreads_wait_mask;	/* sigset with SIGCHLD */
+ 
+ static int linuxthreads_step_pid;	/* current stepped pid */
+ static int linuxthreads_step_signo;	/* current stepped target signal */
+ static int linuxthreads_exit_status;	/* exit status of initial thread */
+ 
+ static int linuxthreads_inferior_pid;	/* temporary internal inferior pid */
+ static int linuxthreads_breakpoint_pid;	/* last pid that hit a breakpoint */
+ static int linuxthreads_attach_pending;	/* attach command without wait */
+ 
+ static int linuxthreads_breakpoints_inserted;	/* any breakpoints inserted */
+ 
+ /* LinuxThreads uses certain signals for communication between
+    processes; we need to tell GDB to pass them through silently to the
+    inferior.  The LinuxThreads library has global variables we can
+    read containing the relevant signal numbers, but since the signal
+    numbers are chosen at run-time, those variables aren't initialized
+    until the shared library's constructors have had a chance to run.  */
+ 
+ struct linuxthreads_signal {
+ 
+   /* The name of the LinuxThreads library variable that contains
+      the signal number.  */
+   char *var;
+ 
+   /* True if this variable must exist for us to debug properly.  */
+   int required;
+   
+   /* The variable's address in the inferior, or zero if the
+      LinuxThreads library hasn't been loaded into this inferior yet.  */
+   CORE_ADDR addr;
+ 
+   /* The signal number, or zero if we don't know yet (either because
+      we haven't found the variable, or it hasn't been initialized).
+      This is an actual target signal number that you could pass to
+      `kill', not a GDB signal number.  */
+   int signal;
+ 
+   /* GDB's original settings for `stop' and `print' for this signal.
+      We restore them when the user selects a different executable.
+      Invariant: if sig->signal != 0, then sig->{stop,print} contain
+      the original settings.  */
+   int stop, print;
+ };
+ 
+ struct linuxthreads_signal linuxthreads_sig_restart = {
+   "__pthread_sig_restart", 1, 0, 0, 0
+ };
+ struct linuxthreads_signal linuxthreads_sig_cancel = {
+   "__pthread_sig_cancel", 1, 0, 0, 0
+ };
+ struct linuxthreads_signal linuxthreads_sig_debug = {
+   "__pthread_sig_debug", 0, 0, 0, 0
+ };
+ 
+ /* A table of breakpoint locations, one per PID.  */
+ static struct linuxthreads_breakpoint {
+     CORE_ADDR	pc;	/* PC of breakpoint */
+     int		pid;	/* pid of breakpoint */
+     int		step;	/* whether the pc has been reached after sstep */
+ } *linuxthreads_breakpoint_zombie;		/* Zombie breakpoints array */
+ static int linuxthreads_breakpoint_last;	/* Last zombie breakpoint */
+ 
+ /* linuxthreads_{insert,remove}_breakpoint pass the breakpoint address
+    to {insert,remove}_breakpoint via this variable, since
+    iterate_active_threads doesn't provide any way to pass values
+    through to the worker function.  */
+ static CORE_ADDR linuxthreads_breakpoint_addr;
+ 
+ #define	REMOVE_BREAKPOINT_ZOMBIE(_i) \
+ { \
+   if ((_i) < linuxthreads_breakpoint_last) \
+     linuxthreads_breakpoint_zombie[(_i)] = \
+       linuxthreads_breakpoint_zombie[linuxthreads_breakpoint_last]; \
+   linuxthreads_breakpoint_last--; \
+ }
+ 
+ 
+ 
+ #ifndef PTRACE_XFER_TYPE
+ #define PTRACE_XFER_TYPE int
+ #endif
+ /* Check to see if the given thread is alive.  */
+ static int
+ linuxthreads_thread_alive (pid)
+      int pid;
+ {
+     errno = 0;
+     return ptrace (PT_READ_U, pid, (PTRACE_ARG3_TYPE)0, 0) >= 0 || errno == 0;
+ }
+ 
+ /* On detach(), find a SIGTRAP status.  If stop is non-zero, find a
+    SIGSTOP one, too.
+ 
+    Make sure PID is ready to run, and free of interference from our
+    efforts to debug it (e.g., pending SIGSTOP or SIGTRAP signals).  If
+    STOP is zero, just look for a SIGTRAP.  If STOP is non-zero, look
+    for a SIGSTOP, too.  Return non-zero if PID is alive and ready to
+    run; return zero if PID is dead.
+ 
+    PID may or may not be stopped at the moment, and we may or may not
+    have waited for it already.  We check the linuxthreads_wait bag in
+    case we've already got a status for it.  We may possibly wait for
+    it ourselves.
+ 
+    PID may have signals waiting to be delivered.  If they're caused by
+    our efforts to debug it, accept them with wait, but don't pass them
+    through to PID.  Do pass all other signals through.  */   
+ static int
+ linuxthreads_find_trap (pid, stop)
+     int pid;
+     int stop;
+ {
+   int i;
+   int rpid;
+   int status;
+   int found_stop = 0;
+   int found_trap = 0;
+ 
+   /* PID may have any number of signals pending.  The kernel will
+      report each of them to us via wait, and then it's up to us to
+      pass them along to the process via ptrace, if we so choose.
+ 
+      We need to paw through the whole set until we've found a SIGTRAP
+      (or a SIGSTOP, if `stop' is set).  We don't pass the SIGTRAP (or
+      SIGSTOP) through, but we do re-send all the others, so PID will
+      receive them when we resume it.  */
+   int *wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int));
+   int last = 0;
+ 
+   /* Look at the pending status */
+   for (i = linuxthreads_wait_last; i >= 0; i--)
+     if (linuxthreads_wait_pid[i] == pid)
+       {
+ 	status = linuxthreads_wait_status[i];
+ 
+ 	/* Delete the i'th member of the table.  Since the table is
+ 	   unordered, we can do this simply by copying the table's
+ 	   last element to the i'th position, and shrinking the table
+ 	   by one element.  */
+ 	if (i < linuxthreads_wait_last)
+ 	  {
+ 	    linuxthreads_wait_status[i] =
+ 	      linuxthreads_wait_status[linuxthreads_wait_last];
+ 	    linuxthreads_wait_pid[i] =
+ 	      linuxthreads_wait_pid[linuxthreads_wait_last];
+ 	  }
+ 	linuxthreads_wait_last--;
+ 
+ 	if (!WIFSTOPPED(status)) /* Thread has died */
+ 	  return 0;
+ 
+ 	if (WSTOPSIG(status) == SIGTRAP)
+ 	  {
+ 	    if (stop)
+ 	      found_trap = 1;
+ 	    else
+ 	      return 1;
+ 	  }
+ 	else if (WSTOPSIG(status) == SIGSTOP)
+ 	  {
+ 	    if (stop)
+ 	      found_stop = 1;
+ 	  }
+ 	else
+ 	  {
+ 	    wstatus[0] = status;
+ 	    last = 1;
+ 	  }
+ 
+ 	break;
+       }
+ 
+   if (stop)
+     {
+       /* Make sure that we'll find what we're looking for.  */
+       if (!found_trap)
+ 	kill (pid, SIGTRAP);
+       if (!found_stop)
+ 	kill (pid, SIGSTOP);
+     }
+ 		      
+   /* Catch all status until SIGTRAP and optionally SIGSTOP show up.  */
+   for (;;)
+     {
+       child_resume (pid, 1, TARGET_SIGNAL_0);
+ 
+       for (;;)
+ 	{
+ 	  rpid = waitpid (pid, &status, __WCLONE);
+ 	  if (rpid > 0)
+ 	    break;
+ 	  if (errno == EINTR)
+ 	    continue;
+ 
+ 	  /* There are a few reasons the wait call above may have
+ 	     failed.  If the thread manager dies, its children get
+ 	     reparented, and this interferes with GDB waiting for
+ 	     them, in some cases.  Another possibility is that the
+ 	     initial thread was not cloned, so calling wait with
+ 	     __WCLONE won't find it.  I think neither of these should
+ 	     occur in modern Linux kernels --- they don't seem to in
+ 	     2.0.36.  */
+ 	  rpid = waitpid (pid, &status, 0);
+ 	  if (rpid > 0)
+ 	    break;
+ 	  if (errno != EINTR)
+ 	    perror_with_name ("waitpid");
+ 	}
+ 
+       if (!WIFSTOPPED(status)) /* Thread has died */
+ 	return 0;
+ 
+       if (WSTOPSIG(status) == SIGTRAP)
+ 	if (!stop || found_stop)
+ 	  break;
+ 	else
+ 	  found_trap = 1;
+       else if (WSTOPSIG(status) != SIGSTOP)
+ 	wstatus[last++] = status;
+       else if (stop)
+ 	if (found_trap)
+ 	  break;
+ 	else
+ 	  found_stop = 1;
+     }
+ 
+   /* Resend any other signals we noticed to the thread, to be received
+      when we continue it.  */
+   while (--last >= 0)
+     kill (pid, WSTOPSIG(wstatus[last]));
+ 
+   return 1;
+ }
+ 
+ /* Cleanup stub for save_inferior_pid.  */
+ static void
+ restore_inferior_pid (arg)
+     void *arg;
+ {
+   int pid = (int) arg;
+   inferior_pid = pid;
+ }
+ 
+ /* Register a cleanup to restore the value of inferior_pid.  */
+ static struct cleanup *
+ save_inferior_pid ()
+ {
+   return make_cleanup (restore_inferior_pid, (void *) inferior_pid);
+ }
+ 
+ static void
+ sigchld_handler(signo)
+     int signo;
+ {
+     /* This handler is used to get an EINTR while doing waitpid()
+        when an event is received */
+ }
+ 
+ /* Have we already collected a wait status for PID in the
+    linuxthreads_wait bag?  */
+ static int
+ linuxthreads_pending_status (pid)
+     int pid;
+ {
+   int i;
+   for (i = linuxthreads_wait_last; i >= 0; i--)
+     if (linuxthreads_wait_pid[i] == pid)
+       return 1;
+   return 0;
+ }
+ 
+ 
+ /* Internal linuxthreads signal management */
+ 
+ /* Check in OBJFILE for the variable that holds the number for signal SIG.
+    We assume that we've already found other LinuxThreads-ish variables
+    in OBJFILE, so we complain if it's required, but not there.
+    Return true iff things are okay.  */
+ static int
+ find_signal_var (struct linuxthreads_signal *sig,
+ 		 struct objfile *objfile)
+ {
+   struct minimal_symbol *ms = lookup_minimal_symbol (sig->var, NULL, objfile);
+ 
+   if (! ms)
+     {
+       if (sig->required)
+ 	{
+ 	  fprintf_unfiltered (gdb_stderr,
+ 			      "Unable to find linuxthreads symbol \"%s\"\n",
+ 			      sig->var);
+ 	  return 0;
+ 	}
+       else
+ 	{
+ 	  sig->addr = 0;
+ 	  return 1;
+ 	}
+     }
+ 
+   sig->addr = SYMBOL_VALUE_ADDRESS (ms);
+ 
+   return 1;
+ }
+ 
+ static int
+ find_all_signal_vars (struct objfile *objfile)
+ {
+   return (   find_signal_var (&linuxthreads_sig_restart, objfile)
+ 	  && find_signal_var (&linuxthreads_sig_cancel,  objfile)
+ 	  && find_signal_var (&linuxthreads_sig_debug,   objfile));
+ }
+ 
+ /* A struct complaint isn't appropriate here.  */
+ static int complained_cannot_determine_thread_signal_number = 0;
+ 
+ /* Check to see if the variable holding the signal number for SIG has
+    been initialized yet.  If it has, tell GDB to pass that signal
+    through to the inferior silently.  */
+ static void
+ check_signal_number (struct linuxthreads_signal *sig)
+ {
+   int num;
+ 
+   if (sig->signal)
+     /* We already know this signal number.  */
+     return;
+ 
+   if (! sig->addr)
+     /* We don't know the variable's address yet.  */
+     return;
+ 
+   if (target_read_memory (sig->addr, (char *)&num, sizeof (num))
+       != 0)
+     {
+       /* If this happens once, it'll probably happen for all the
+ 	 signals, so only complain once.  */
+       if (! complained_cannot_determine_thread_signal_number)
+ 	warning ("Cannot determine thread signal number; "
+ 		 "GDB may report spurious signals.");
+       complained_cannot_determine_thread_signal_number = 1;
+       return;
+     }
+   
+   if (num == 0)
+     /* It hasn't been initialized yet.  */
+     return;
+ 
+   /* We know sig->signal was zero, and is becoming non-zero, so it's
+      okay to sample GDB's original settings.  */
+   sig->signal = num;
+   sig->stop  = signal_stop_update  (target_signal_from_host (num), 0);
+   sig->print = signal_print_update (target_signal_from_host (num), 0);
+ }
+ 
+ 
+ static void
+ check_all_signal_numbers (void)
+ {
+   /* If this isn't a LinuxThreads program, quit early.  */
+   if (! linuxthreads_max)
+     return;
+ 
+   check_signal_number (&linuxthreads_sig_restart);
+   check_signal_number (&linuxthreads_sig_cancel);
+   check_signal_number (&linuxthreads_sig_debug);
+ 
+   /* handle linuxthread exit */
+   if (linuxthreads_sig_debug.signal
+       || linuxthreads_sig_restart.signal)
+     {
+       struct sigaction sact;
+ 
+       sact.sa_handler = sigchld_handler;
+       sigemptyset(&sact.sa_mask);
+       sact.sa_flags = 0;
+       if (linuxthreads_sig_debug.signal > 0)
+ 	sigaction(linuxthreads_sig_cancel.signal, &sact, NULL);
+       else
+ 	sigaction(linuxthreads_sig_restart.signal, &sact, NULL);
+     }
+ }
+ 
+ 
+ /* Restore GDB's original settings for SIG.
+    This should only be called when we're no longer sure if we're
+    talking to an executable that uses LinuxThreads, so we clear the
+    signal number and variable address too.  */
+ static void
+ restore_signal (struct linuxthreads_signal *sig)
+ {
+   if (! sig->signal)
+     return;
+ 
+   /* We know sig->signal was non-zero, and is becoming zero, so it's
+      okay to restore GDB's original settings.  */
+   signal_stop_update  (target_signal_from_host (sig->signal), sig->stop);
+   signal_print_update (target_signal_from_host (sig->signal), sig->print);
+ 
+   sig->signal = 0;
+   sig->addr = 0;
+ }
+ 
+ 
+ /* Restore GDB's original settings for all LinuxThreads signals.
+    This should only be called when we're no longer sure if we're
+    talking to an executable that uses LinuxThreads, so we clear the
+    signal number and variable address too.  */
+ static void
+ restore_all_signals (void)
+ {
+   restore_signal (&linuxthreads_sig_restart);
+   restore_signal (&linuxthreads_sig_cancel);
+   restore_signal (&linuxthreads_sig_debug);
+ 
+   /* If it happens again, we should complain again.  */
+   complained_cannot_determine_thread_signal_number = 0;
+ }
+ 
+ 
+ 
+ 
+ /* Apply FUNC to the pid of each active thread.  This consults the
+    inferior's handle table to find active threads.
+ 
+    If ALL is non-zero, process all threads.
+    If ALL is zero, skip threads with pending status.  */
+ static void
+ iterate_active_threads (func, all)
+     void (*func)(int);
+     int all;
+ {
+   CORE_ADDR descr;
+   int pid;
+   int i;
+   int num;
+ 
+   read_memory (linuxthreads_num, (char *)&num, sizeof (int));
+ 
+   for (i = 0; i < linuxthreads_max && num > 0; i++)
+     {
+       read_memory (linuxthreads_handles +
+ 		   linuxthreads_sizeof_handle * i + linuxthreads_offset_descr,
+ 		   (char *)&descr, sizeof (void *));
+       if (descr)
+ 	{
+ 	  num--;
+ 	  read_memory (descr + linuxthreads_offset_pid,
+ 		       (char *)&pid, sizeof (pid_t));
+ 	  if (pid > 0 && pid != linuxthreads_manager_pid
+ 	      && (all || (!linuxthreads_pending_status (pid))))
+ 	    (*func)(pid);
+ 	}
+     }
+ 
+ }
+ 
+ /* Insert a thread breakpoint at linuxthreads_breakpoint_addr.
+    This is the worker function for linuxthreads_insert_breakpoint,
+    which passes it to iterate_active_threads.  */
+ static void
+ insert_breakpoint (pid)
+     int pid;
+ {
+   int j;
+ 
+   /* Remove (if any) the positive zombie breakpoint.  */
+   for (j = linuxthreads_breakpoint_last; j >= 0; j--)
+     if (linuxthreads_breakpoint_zombie[j].pid == pid)
+       {
+ 	if ((linuxthreads_breakpoint_zombie[j].pc - DECR_PC_AFTER_BREAK
+ 	     == linuxthreads_breakpoint_addr)
+ 	    && !linuxthreads_breakpoint_zombie[j].step)
+ 	  REMOVE_BREAKPOINT_ZOMBIE(j);
+ 	break;
+       }
+ }
+ 
+ /* Note that we're about to remove a thread breakpoint at
+    linuxthreads_breakpoint_addr.
+ 
+    This is the worker function for linuxthreads_remove_breakpoint,
+    which passes it to iterate_active_threads.  The actual work of
+    overwriting the breakpoint instruction is done by
+    child_ops.to_remove_breakpoint; here, we simply create a zombie
+    breakpoint if the thread's PC is pointing at the breakpoint being
+    removed.  */
+ static void
+ remove_breakpoint (pid)
+     int pid;
+ {
+   int j;
+ 
+   /* Insert a positive zombie breakpoint (if needed).  */
+   for (j = 0; j <= linuxthreads_breakpoint_last; j++)
+     if (linuxthreads_breakpoint_zombie[j].pid == pid)
+       break;
+ 
+   if (in_thread_list (pid) && linuxthreads_thread_alive (pid))
+     {
+       CORE_ADDR pc = read_pc_pid (pid);
+       if (linuxthreads_breakpoint_addr == pc - DECR_PC_AFTER_BREAK
+ 	  && j > linuxthreads_breakpoint_last)
+ 	{
+ 	  linuxthreads_breakpoint_zombie[j].pid = pid;
+ 	  linuxthreads_breakpoint_zombie[j].pc = pc;
+ 	  linuxthreads_breakpoint_zombie[j].step = 0;
+ 	  linuxthreads_breakpoint_last++;
+ 	}
+     }
+ }
+ 
+ /* Kill a thread */
+ static void
+ kill_thread (pid)
+     int pid;
+ {
+   if (in_thread_list (pid))
+     ptrace (PT_KILL, pid, (PTRACE_ARG3_TYPE) 0, 0);
+   else
+     kill (pid, SIGKILL);
+ }
+ 
+ /* Resume a thread */
+ static void
+ resume_thread (pid)
+     int pid;
+ {
+   if (pid != inferior_pid
+       && in_thread_list (pid)
+       && linuxthreads_thread_alive (pid))
+     if (pid == linuxthreads_step_pid)
+       child_resume (pid, 1, linuxthreads_step_signo);
+     else
+       child_resume (pid, 0, TARGET_SIGNAL_0);
+ }
+ 
+ /* Detach a thread */
+ static void
+ detach_thread (pid)
+     int pid;
+ {
+   if (in_thread_list (pid) && linuxthreads_thread_alive (pid))
+     {
+       /* Remove pending SIGTRAP and SIGSTOP */
+       linuxthreads_find_trap (pid, 1);
+ 
+       inferior_pid = pid;
+       detach (TARGET_SIGNAL_0);
+       inferior_pid = linuxthreads_manager_pid;
+     }
+ }
+ 
+ /* Stop a thread */
+ static void
+ stop_thread (pid)
+     int pid;
+ {
+   if (pid != inferior_pid)
+     if (in_thread_list (pid))
+       kill (pid, SIGSTOP);
+     else if (ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0) == 0)
+       {
+ 	if (!linuxthreads_attach_pending)
+ 	  printf_unfiltered ("[New %s]\n", target_pid_to_str (pid));
+ 	add_thread (pid);
+ 	if (linuxthreads_sig_debug.signal)
+ 	  /* After a new thread in glibc 2.1 signals gdb its existence,
+ 	     it suspends itself and wait for linuxthreads_sig_restart,
+ 	     now we can wake up it. */
+ 	  kill (pid, linuxthreads_sig_restart.signal);
+       }
+     else
+       perror_with_name ("ptrace in stop_thread");
+ }
+ 
+ /* Wait for a thread */
+ static void
+ wait_thread (pid)
+     int pid;
+ {
+   int status;
+   int rpid;
+ 
+   if (pid != inferior_pid && in_thread_list (pid))
+     {
+       for (;;)
+ 	{
+ 	  /* Get first pid status.  */
+ 	  rpid = waitpid(pid, &status, __WCLONE);
+ 	  if (rpid > 0)
+ 	    break;
+ 	  if (errno == EINTR)
+ 	    continue;
+ 
+ 	  /* There are two reasons this might have failed:
+ 
+ 	     1) PID is the initial thread, which wasn't cloned, so
+ 	     passing the __WCLONE flag to waitpid prevented us from
+ 	     finding it.
+ 
+ 	     2) The manager thread is the parent of all but the
+ 	     initial thread; if it dies, the children will all be
+ 	     reparented to init, which will wait for them.  This means
+ 	     our call to waitpid won't find them.
+ 
+ 	     Actually, based on a casual look at the 2.0.36 kernel
+ 	     code, I don't think either of these cases happen.  But I
+ 	     don't have things set up for remotely debugging the
+ 	     kernel, so I'm not sure.  And perhaps older kernels
+ 	     didn't work.  */
+ 	  rpid = waitpid(pid, &status, 0);
+ 	  if (rpid > 0)
+ 	    break;
+ 	  if (errno != EINTR && linuxthreads_thread_alive (pid))
+ 	    perror_with_name ("waitpid");
+ 
+ 	  /* the thread is dead.  */
+ 	  return;
+ 	}
+       if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
+ 	{
+ 	  linuxthreads_wait_pid[++linuxthreads_wait_last] = pid;
+ 	  linuxthreads_wait_status[linuxthreads_wait_last] = status;
+ 	}
+     }
+ }
+ 
+ /* Walk through the linuxthreads handles in order to detect all
+    threads and stop them */
+ static void
+ update_stop_threads (test_pid)
+     int test_pid;
+ {
+   struct cleanup *old_chain = NULL;
+ 
+   check_all_signal_numbers ();
+ 
+   if (linuxthreads_manager_pid == 0)
+     {
+       if (linuxthreads_manager)
+ 	{
+ 	  if (test_pid > 0 && test_pid != inferior_pid)
+ 	    {
+ 	      old_chain = save_inferior_pid ();
+ 	      inferior_pid = test_pid;
+ 	    }
+ 	  read_memory (linuxthreads_manager,
+ 		       (char *)&linuxthreads_manager_pid, sizeof (pid_t));
+ 	}
+       if (linuxthreads_initial)
+ 	{
+ 	  if (test_pid > 0 && test_pid != inferior_pid)
+ 	    {
+ 	      old_chain = save_inferior_pid ();
+ 	      inferior_pid = test_pid;
+ 	    }
+ 	  read_memory(linuxthreads_initial,
+ 		      (char *)&linuxthreads_initial_pid, sizeof (pid_t));
+ 	}
+     }
+ 
+   if (linuxthreads_manager_pid != 0)
+     {
+       if (old_chain == NULL && test_pid > 0 &&
+ 	  test_pid != inferior_pid && linuxthreads_thread_alive (test_pid))
+ 	{
+ 	  old_chain = save_inferior_pid ();
+ 	  inferior_pid = test_pid;
+ 	}
+ 
+       if (linuxthreads_thread_alive (inferior_pid))
+ 	{
+ 	  if (test_pid > 0)
+ 	    {
+ 	      if (test_pid != linuxthreads_manager_pid
+ 		  && !linuxthreads_pending_status (linuxthreads_manager_pid))
+ 		{
+ 		  stop_thread (linuxthreads_manager_pid);
+ 		  wait_thread (linuxthreads_manager_pid);
+ 		}
+ 	      if (!in_thread_list (test_pid))
+ 	        {
+ 		  if (!linuxthreads_attach_pending)
+ 		    printf_unfiltered ("[New %s]\n",
+ 				       target_pid_to_str (test_pid));
+ 		  add_thread (test_pid);
+ 		  if (linuxthreads_sig_debug.signal
+ 		      && inferior_pid == test_pid)
+ 		    /* After a new thread in glibc 2.1 signals gdb its
+ 		       existence, it suspends itself and wait for
+ 		       linuxthreads_sig_restart, now we can wake up
+ 		       it. */
+ 		    kill (test_pid, linuxthreads_sig_restart.signal);
+ 		}
+ 	    }
+ 	  iterate_active_threads (stop_thread, 0);
+ 	  iterate_active_threads (wait_thread, 0);
+ 	}
+     }
+ 
+   if (old_chain != NULL)
+     do_cleanups (old_chain);
+ }
+ 
+ /* This routine is called whenever a new symbol table is read in, or when all
+    symbol tables are removed.  libpthread can only be initialized when it
+    finds the right variables in libpthread.so.  Since it's a shared library,
+    those variables don't show up until the library gets mapped and the symbol
+    table is read in.  */
+ 
+ void
+ linuxthreads_new_objfile (objfile)
+     struct objfile *objfile;
+ {
+   struct minimal_symbol *ms;
+ 
+   if (!objfile)
+     {
+       /* We're starting an entirely new executable, so we can no
+ 	 longer be sure that it uses LinuxThreads.  Restore the signal
+ 	 flags to their original states.  */
+       restore_all_signals ();
+ 
+       /* Indicate that we don't know anything's address any more.  */
+       linuxthreads_max = 0;
+ 
+       return;
+     }
+ 
+   /* If we've already found our variables in another objfile, don't
+      bother looking for them again.  */
+   if (linuxthreads_max)
+     return;
+ 
+   if (! lookup_minimal_symbol ("__pthread_initial_thread", NULL, objfile))
+     /* This object file isn't the pthreads library.  */
+     return;
+ 
+   if ((ms = lookup_minimal_symbol ("__pthread_threads_debug",
+ 				   NULL, objfile)) == NULL)
+     {
+       /* The debugging-aware libpthreads is not present in this objfile */
+       warning ("\
+ This program seems to use POSIX threads, but the thread library used\n\
+ does not support debugging.  This may make using GDB difficult.  Don't\n\
+ set breakpoints or single-step through code that might be executed by\n\
+ any thread other than the main thread.");
+       return;
+     }
+   linuxthreads_debug = SYMBOL_VALUE_ADDRESS (ms);
+ 
+   /* Read internal structures configuration */
+   if ((ms = lookup_minimal_symbol ("__pthread_sizeof_handle",
+ 				   NULL, objfile)) == NULL
+       || target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
+ 			     (char *)&linuxthreads_sizeof_handle,
+ 			     sizeof (linuxthreads_sizeof_handle)) != 0)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_sizeof_handle");
+       return;
+     }
+ 
+   if ((ms = lookup_minimal_symbol ("__pthread_offsetof_descr",
+ 				   NULL, objfile)) == NULL
+       || target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
+ 			     (char *)&linuxthreads_offset_descr,
+ 			     sizeof (linuxthreads_offset_descr)) != 0)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_offsetof_descr");
+       return;
+     }
+ 	 
+   if ((ms = lookup_minimal_symbol ("__pthread_offsetof_pid",
+ 				   NULL, objfile)) == NULL
+       || target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
+ 			     (char *)&linuxthreads_offset_pid,
+ 			     sizeof (linuxthreads_offset_pid)) != 0)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_offsetof_pid");
+       return;
+     }
+ 
+   if (! find_all_signal_vars (objfile))
+     return;
+ 
+   /* Read adresses of internal structures to access */
+   if ((ms = lookup_minimal_symbol ("__pthread_handles",
+ 				   NULL, objfile)) == NULL)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_handles");
+       return;
+     }
+   linuxthreads_handles = SYMBOL_VALUE_ADDRESS (ms);
+ 
+   if ((ms = lookup_minimal_symbol ("__pthread_handles_num",
+ 				   NULL, objfile)) == NULL)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_handles_num");
+       return;
+     }
+   linuxthreads_num = SYMBOL_VALUE_ADDRESS (ms);
+ 
+   if ((ms = lookup_minimal_symbol ("__pthread_manager_thread",
+ 				   NULL, objfile)) == NULL)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_manager_thread");
+       return;
+     }
+   linuxthreads_manager = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid;
+ 
+   if ((ms = lookup_minimal_symbol ("__pthread_initial_thread",
+ 				   NULL, objfile)) == NULL)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_initial_thread");
+       return;
+     }
+   linuxthreads_initial = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid;
+ 
+   /* Search for this last, so it won't be set to a non-zero value unless
+      we successfully found all the symbols above.  */
+   if ((ms = lookup_minimal_symbol ("__pthread_threads_max",
+ 				   NULL, objfile)) == NULL
+       || target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
+ 			     (char *)&linuxthreads_max,
+ 			     sizeof (linuxthreads_max)) != 0)
+     {
+       fprintf_unfiltered (gdb_stderr,
+ 			  "Unable to find linuxthreads symbol \"%s\"\n",
+ 			  "__pthread_threads_max");
+       return;
+     }
+ 
+   /* Allocate gdb internal structures */
+   linuxthreads_wait_pid = 
+     (int *)xmalloc (sizeof (int) * (linuxthreads_max + 1));
+   linuxthreads_wait_status =
+     (int *)xmalloc (sizeof (int) * (linuxthreads_max + 1));
+   linuxthreads_breakpoint_zombie = (struct linuxthreads_breakpoint *)
+     xmalloc (sizeof (struct linuxthreads_breakpoint) * (linuxthreads_max + 1));
+ 
+   if (inferior_pid && !linuxthreads_attach_pending)
+     {
+       int on = 1;
+       target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on));
+       linuxthreads_attach_pending = 1;
+       update_stop_threads (inferior_pid);
+       linuxthreads_attach_pending = 0;
+     }
+ }
+ 
+ /* If we have switched threads from a one that stopped at breakpoint,
+    return 1 otherwise 0.  */
+ 
+ int
+ linuxthreads_prepare_to_proceed (step)
+     int step;
+ {
+   if (!linuxthreads_max
+       || !linuxthreads_manager_pid
+       || !linuxthreads_breakpoint_pid
+       || !breakpoint_here_p (read_pc_pid (linuxthreads_breakpoint_pid)))
+     return 0;
+ 
+   if (step)
+     {
+       /* Mark the current inferior as single stepping process.  */
+       linuxthreads_step_pid = inferior_pid;
+     }
+ 
+   linuxthreads_inferior_pid = linuxthreads_breakpoint_pid;
+   return linuxthreads_breakpoint_pid;
+ }
+ 
+ /* Convert a pid to printable form. */
+ 
+ char *
+ linuxthreads_pid_to_str (pid)
+     int pid;
+ {
+   static char buf[100];
+ 
+   sprintf (buf, "%s %d%s", linuxthreads_max ? "Thread" : "Pid", pid,
+ 	   (pid == linuxthreads_manager_pid) ? " (manager thread)"
+ 	   : (pid == linuxthreads_initial_pid) ? " (initial thread)"
+ 	   : "");
+ 
+   return buf;
+ }
+ 
+ /* Attach to process PID, then initialize for debugging it
+    and wait for the trace-trap that results from attaching.  */
+ 
+ static void
+ linuxthreads_attach (args, from_tty)
+     char *args;
+     int from_tty;
+ {
+   if (!args)
+     error_no_arg ("process-id to attach");
+ 
+   push_target (&linuxthreads_ops);
+   linuxthreads_breakpoints_inserted = 1;
+   linuxthreads_breakpoint_last = -1;
+   linuxthreads_wait_last = -1;
+   linuxthreads_exit_status = __W_STOPCODE(0);
+ 
+   child_ops.to_attach (args, from_tty);
+ 
+   if (linuxthreads_max)
+     linuxthreads_attach_pending = 1;
+ }
+ 
+ /* Take a program previously attached to and detaches it.
+    The program resumes execution and will no longer stop
+    on signals, etc.  We'd better not have left any breakpoints
+    in the program or it'll die when it hits one.  For this
+    to work, it may be necessary for the process to have been
+    previously attached.  It *might* work if the program was
+    started via the normal ptrace (PTRACE_TRACEME).  */
+ 
+ static void
+ linuxthreads_detach (args, from_tty)
+     char *args;
+     int from_tty;
+ {
+   if (linuxthreads_max)
+     {
+       int i;
+       int pid;
+       int off = 0;
+       target_write_memory (linuxthreads_debug, (char *)&off, sizeof (off));
+ 
+       /* Walk through linuxthreads array in order to detach known threads.  */
+       if (linuxthreads_manager_pid != 0)
+ 	{
+ 	  /* Get rid of all positive zombie breakpoints.  */
+ 	  for (i = 0; i <= linuxthreads_breakpoint_last; i++)
+ 	    {
+ 	      if (linuxthreads_breakpoint_zombie[i].step)
+ 		continue;
+ 
+ 	      pid = linuxthreads_breakpoint_zombie[i].pid;
+ 	      if (!linuxthreads_thread_alive (pid))
+ 		continue;
+ 
+ 	      if (linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (pid))
+ 		continue;
+ 
+ 	      /* Continue in STEP mode until the thread pc has moved or
+ 		 until SIGTRAP is found on the same PC.  */
+ 	      if (linuxthreads_find_trap (pid, 0)
+ 		  && linuxthreads_breakpoint_zombie[i].pc == read_pc_pid (pid))
+ 		write_pc_pid (linuxthreads_breakpoint_zombie[i].pc
+ 			      - DECR_PC_AFTER_BREAK, pid);
+ 	    }
+ 
+ 	  /* Detach thread after thread.  */
+ 	  inferior_pid = linuxthreads_manager_pid;
+ 	  iterate_active_threads (detach_thread, 1);
+ 
+ 	  /* Remove pending SIGTRAP and SIGSTOP */
+ 	  linuxthreads_find_trap (inferior_pid, 1);
+ 
+ 	  linuxthreads_wait_last = -1;
+ 	  linuxthreads_exit_status = __W_STOPCODE(0);
+ 	}
+ 
+       linuxthreads_inferior_pid = 0;
+       linuxthreads_breakpoint_pid = 0;
+       linuxthreads_step_pid = 0;
+       linuxthreads_step_signo = TARGET_SIGNAL_0;
+       linuxthreads_manager_pid = 0;
+       linuxthreads_initial_pid = 0;
+       linuxthreads_attach_pending = 0;
+       init_thread_list ();           /* Destroy thread info */
+     }
+ 
+   child_ops.to_detach (args, from_tty);
+ 
+   unpush_target (&linuxthreads_ops);
+ }
+ 
+ /* Resume execution of process PID.  If STEP is nozero, then
+    just single step it.  If SIGNAL is nonzero, restart it with that
+    signal activated.  */
+ 
+ static void
+ linuxthreads_resume (pid, step, signo)
+     int pid;
+     int step;
+     enum target_signal signo;
+ {
+   if (!linuxthreads_max || stop_soon_quietly || linuxthreads_manager_pid == 0)
+     child_ops.to_resume (pid, step, signo);
+   else
+     {
+       int rpid;
+       if (linuxthreads_inferior_pid)
+ 	{
+ 	  /* Prepare resume of the last thread that hit a breakpoint */
+ 	  linuxthreads_breakpoints_inserted = 0;
+ 	  rpid = linuxthreads_inferior_pid;
+ 	  linuxthreads_step_signo = signo;
+ 	}
+       else
+         {
+ 	  struct cleanup *old_chain = NULL;
+ 	  int i;
+ 
+ 	  if (pid < 0)
+ 	    {
+ 	      linuxthreads_step_pid = step ? inferior_pid : 0;
+ 	      linuxthreads_step_signo = signo;
+ 	      rpid = inferior_pid;
+ 	    }
+ 	  else
+ 	    rpid = pid;
+ 
+ 	  if (pid < 0 || !step)
+ 	    {
+ 	      linuxthreads_breakpoints_inserted = 1;
+ 
+ 	      /* Walk through linuxthreads array in order to resume threads */
+ 	      if (pid >= 0 && inferior_pid != pid)
+ 		{
+ 		  old_chain = save_inferior_pid ();
+ 		  inferior_pid = pid;
+ 		}
+ 
+ 	      iterate_active_threads (resume_thread, 0);
+ 	      if (linuxthreads_manager_pid != inferior_pid
+ 		  && !linuxthreads_pending_status (linuxthreads_manager_pid))
+ 		resume_thread (linuxthreads_manager_pid);
+ 	    }
+ 	  else
+ 	    linuxthreads_breakpoints_inserted = 0;
+ 
+ 	  /* Deal with zombie breakpoint */
+ 	  for (i = 0; i <= linuxthreads_breakpoint_last; i++)
+ 	    if (linuxthreads_breakpoint_zombie[i].pid == rpid)
+ 	      {
+ 		if (linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (rpid))
+ 		  {
+ 		    /* The current pc is out of zombie breakpoint.  */
+ 		    REMOVE_BREAKPOINT_ZOMBIE(i);
+ 		  }
+ 		break;
+ 	      }
+ 
+ 	  if (old_chain != NULL)
+ 	    do_cleanups (old_chain);
+ 	}
+ 
+       /* Resume initial thread. */
+       if (!linuxthreads_pending_status (rpid))
+ 	child_ops.to_resume (rpid, step, signo);
+     }
+ }
+ 
+ /* Wait for any threads to stop.  We may have to convert PID from a thread id
+    to a LWP id, and vice versa on the way out.  */
+ 
+ static int
+ linuxthreads_wait (int pid, struct target_waitstatus *ourstatus)
+ {
+   int status;
+   int rpid;
+   int i;
+   int last;
+   int *wstatus;
+ 
+   if (linuxthreads_max && !linuxthreads_breakpoints_inserted)
+     wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int));
+ 
+   /* See if the inferior has chosen values for its signals yet.  By
+      checking for them here, we can be sure we've updated GDB's signal
+      handling table before the inferior ever gets one of them.  (Well,
+      before we notice, anyway.)  */
+   check_all_signal_numbers ();
+ 
+   for (;;)
+     {
+       if (!linuxthreads_max)
+ 	  rpid = 0;
+       else if (!linuxthreads_breakpoints_inserted)
+ 	{
+ 	  if (linuxthreads_inferior_pid)
+ 	    pid = linuxthreads_inferior_pid;
+ 	  else if (pid < 0)
+ 	    pid = inferior_pid;
+ 	  last = rpid = 0;
+ 	}
+       else if (pid < 0 && linuxthreads_wait_last >= 0)
+ 	{
+ 	  status = linuxthreads_wait_status[linuxthreads_wait_last];
+ 	  rpid = linuxthreads_wait_pid[linuxthreads_wait_last--];
+         }
+       else if (pid > 0 && linuxthreads_pending_status (pid))
+ 	{
+ 	  for (i = linuxthreads_wait_last; i >= 0; i--)
+ 	    if (linuxthreads_wait_pid[i] == pid)
+ 		break;
+ 	  if (i < 0)
+ 	    rpid = 0;
+ 	  else
+ 	    {
+ 	      status = linuxthreads_wait_status[i];
+ 	      rpid = pid;
+ 	      if (i < linuxthreads_wait_last)
+ 		{
+ 		  linuxthreads_wait_status[i] =
+ 		    linuxthreads_wait_status[linuxthreads_wait_last];
+ 		  linuxthreads_wait_pid[i] =
+ 		    linuxthreads_wait_pid[linuxthreads_wait_last];
+ 		}
+ 	      linuxthreads_wait_last--;
+ 	    }
+ 	}
+       else
+ 	  rpid = 0;
+ 
+       if (rpid == 0)
+ 	{
+ 	  int save_errno;
+ 	  sigset_t omask;
+ 
+ 	  set_sigint_trap();	/* Causes SIGINT to be passed on to the
+ 				   attached process. */
+ 	  set_sigio_trap ();
+ 
+ 	  sigprocmask(SIG_BLOCK, &linuxthreads_wait_mask, &omask);
+ 	  for (;;)
+ 	    {
+ 	      rpid = waitpid (pid, &status, __WCLONE | WNOHANG);
+ 	      if (rpid > 0)
+ 		break;
+ 	      if (rpid == 0)
+ 		save_errno = 0;
+ 	      else if (errno != EINTR)
+ 		save_errno = errno;
+ 	      else
+ 		continue;
+ 
+ 	      rpid = waitpid (pid, &status, WNOHANG);
+ 	      if (rpid > 0)
+ 		break;
+ 	      if (rpid < 0)
+ 		if (errno == EINTR)
+ 		  continue;
+ 		else if (save_errno != 0)
+ 		  break;
+ 
+ 	      sigsuspend(&omask);
+ 	    }
+ 	  sigprocmask(SIG_SETMASK, &omask, NULL);
+ 
+ 	  save_errno = errno;
+ 	  clear_sigio_trap ();
+ 
+ 	  clear_sigint_trap();
+ 
+ 	  if (rpid == -1)
+ 	    {
+ 	      if (WIFEXITED(linuxthreads_exit_status))
+ 		{
+ 		  store_waitstatus (ourstatus, linuxthreads_exit_status);
+ 		  return inferior_pid;
+ 		}
+ 	      else
+ 		{
+ 		  fprintf_unfiltered
+ 		      (gdb_stderr, "Child process unexpectedly missing: %s.\n",
+ 		       safe_strerror (save_errno));
+ 		  /* Claim it exited with unknown signal.  */
+ 		  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ 		  ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+ 		  return -1;
+ 		}
+ 	    }
+ 
+ 	  /* Signals arrive in any order.  So get all signals until SIGTRAP
+ 	     and resend previous ones to be held after.  */
+ 	  if (linuxthreads_max
+ 	      && !linuxthreads_breakpoints_inserted
+ 	      && WIFSTOPPED(status))
+ 	    if (WSTOPSIG(status) == SIGTRAP)
+ 	      {
+ 		while (--last >= 0)
+ 		  kill (rpid, WSTOPSIG(wstatus[last]));
+ 
+ 		/* insert negative zombie breakpoint */
+ 		for (i = 0; i <= linuxthreads_breakpoint_last; i++)
+ 		  if (linuxthreads_breakpoint_zombie[i].pid == rpid)
+ 		      break;
+ 		if (i > linuxthreads_breakpoint_last)
+ 		  {
+ 		    linuxthreads_breakpoint_zombie[i].pid = rpid;
+ 		    linuxthreads_breakpoint_last++;
+ 		  }
+ 		linuxthreads_breakpoint_zombie[i].pc = read_pc_pid (rpid);
+ 		linuxthreads_breakpoint_zombie[i].step = 1;
+ 	      }
+ 	    else
+ 	      {
+ 		if (WSTOPSIG(status) != SIGSTOP)
+ 		  {
+ 		    for (i = 0; i < last; i++)
+ 		      if (wstatus[i] == status)
+ 			break;
+ 		    if (i >= last)
+ 		      wstatus[last++] = status;
+ 		  }
+ 		child_resume (rpid, 1, TARGET_SIGNAL_0);
+ 		continue;
+ 	      }
+ 	  if (linuxthreads_inferior_pid)
+ 	    linuxthreads_inferior_pid = 0;
+ 	}
+ 
+       if (linuxthreads_max && !stop_soon_quietly)
+ 	{
+ 	  if (linuxthreads_max
+ 	      && WIFSTOPPED(status)
+ 	      && WSTOPSIG(status) == SIGSTOP)
+ 	    {
+ 	      /* Skip SIGSTOP signals.  */
+ 	      if (!linuxthreads_pending_status (rpid))
+ 		if (linuxthreads_step_pid == rpid)
+ 		  child_resume (rpid, 1, linuxthreads_step_signo);
+ 		else
+ 		  child_resume (rpid, 0, TARGET_SIGNAL_0);
+ 	      continue;
+ 	    }
+ 
+ 	  /* Do no report exit status of cloned threads.  */
+ 	  if (WIFEXITED(status))
+ 	    {
+ 	      if (rpid == linuxthreads_initial_pid)
+ 		linuxthreads_exit_status = status;
+ 
+ 	      /* Remove any zombie breakpoint.  */
+ 	      for (i = 0; i <= linuxthreads_breakpoint_last; i++)
+ 		if (linuxthreads_breakpoint_zombie[i].pid == rpid)
+ 		  {
+ 		    REMOVE_BREAKPOINT_ZOMBIE(i);
+ 		    break;
+ 		  }
+ 	      if (pid > 0)
+ 		pid = -1;
+ 	      continue;
+ 	    }
+ 
+ 	  /* Deal with zombie breakpoint */
+ 	  for (i = 0; i <= linuxthreads_breakpoint_last; i++)
+ 	    if (linuxthreads_breakpoint_zombie[i].pid == rpid)
+ 	      break;
+ 
+ 	  if (i <= linuxthreads_breakpoint_last)
+ 	    {
+ 	      /* There is a potential zombie breakpoint */
+ 	      if (WIFEXITED(status)
+ 		  || linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (rpid))
+ 	        {
+ 		  /* The current pc is out of zombie breakpoint.  */
+ 		  REMOVE_BREAKPOINT_ZOMBIE(i);
+ 		}
+ 	      else if (!linuxthreads_breakpoint_zombie[i].step
+ 		       && WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP)
+ 	        {
+ 		  /* This is a real one ==> decrement PC and restart.  */
+ 		  write_pc_pid (linuxthreads_breakpoint_zombie[i].pc
+ 				- DECR_PC_AFTER_BREAK, rpid);
+ 		  if (linuxthreads_step_pid == rpid)
+ 		    child_resume (rpid, 1, linuxthreads_step_signo);
+ 		  else
+ 		    child_resume (rpid, 0, TARGET_SIGNAL_0);
+ 		  continue;
+ 		}
+ 	    }
+ 
+ 	  /* Walk through linuxthreads array in order to stop them */
+ 	  if (linuxthreads_breakpoints_inserted)
+ 	    update_stop_threads (rpid);
+ 
+ 	}
+       else if (rpid != inferior_pid)
+ 	continue;
+ 
+       store_waitstatus (ourstatus, status);
+ 
+       if (linuxthreads_attach_pending && !stop_soon_quietly)
+         {
+ 	  int on = 1;
+ 	  target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on));
+ 	  update_stop_threads (rpid);
+ 	  linuxthreads_attach_pending = 0;
+         }
+ 
+       if (linuxthreads_breakpoints_inserted
+ 	  && WIFSTOPPED(status)
+ 	  && WSTOPSIG(status) == SIGTRAP)
+ 	linuxthreads_breakpoint_pid = rpid;
+       else if (linuxthreads_breakpoint_pid)
+ 	linuxthreads_breakpoint_pid = 0;
+ 
+       return rpid;
+     }
+ }
+ 
+ /* Fork an inferior process, and start debugging it with ptrace.  */
+ 
+ static void
+ linuxthreads_create_inferior (exec_file, allargs, env)
+     char *exec_file;
+     char *allargs;
+     char **env;
+ {
+   if (!exec_file && !exec_bfd)
+     {
+       error ("No executable file specified.\n\
+ Use the \"file\" or \"exec-file\" command.");
+       return;
+     }
+ 
+   push_target (&linuxthreads_ops);
+   linuxthreads_breakpoints_inserted = 1;
+   linuxthreads_breakpoint_last = -1;
+   linuxthreads_wait_last = -1;
+   linuxthreads_exit_status = __W_STOPCODE(0);
+   
+   if (linuxthreads_max)
+     linuxthreads_attach_pending = 1;
+ 
+   child_ops.to_create_inferior (exec_file, allargs, env);
+ }
+ 
+ /* Clean up after the inferior dies.  */
+ 
+ static void
+ linuxthreads_mourn_inferior ()
+ {
+   if (linuxthreads_max)
+     {
+       int off = 0;
+       target_write_memory (linuxthreads_debug, (char *)&off, sizeof (off));
+ 
+       linuxthreads_inferior_pid = 0;
+       linuxthreads_breakpoint_pid = 0;
+       linuxthreads_step_pid = 0;
+       linuxthreads_step_signo = TARGET_SIGNAL_0;
+       linuxthreads_manager_pid = 0;
+       linuxthreads_initial_pid = 0;
+       linuxthreads_attach_pending = 0;
+       init_thread_list();           /* Destroy thread info */
+     }
+ 
+   child_ops.to_mourn_inferior ();
+ 
+   unpush_target (&linuxthreads_ops);
+ }
+ 
+ /* Kill the inferior process */
+ 
+ static void
+ linuxthreads_kill ()
+ {
+   int rpid;
+   int status;
+ 
+   if (inferior_pid == 0)
+     return;
+ 
+   if (linuxthreads_max && linuxthreads_manager_pid != 0)
+     {
+       /* Remove all threads status.  */
+       inferior_pid = linuxthreads_manager_pid;
+       iterate_active_threads (kill_thread, 1);
+     }
+ 
+   kill_thread (inferior_pid);
+ 
+ #if 0
+   /* doing_quit_force solves a real problem, but I think a properly
+      placed call to catch_errors would do the trick much more cleanly.  */
+   if (doing_quit_force >= 0)
+     {
+       if (linuxthreads_max && linuxthreads_manager_pid != 0)
+ 	{
+ 	  /* Wait for thread to complete */
+ 	  while ((rpid = waitpid (-1, &status, __WCLONE)) > 0)
+ 	    if (!WIFEXITED(status))
+ 	      kill_thread (rpid);
+ 
+ 	  while ((rpid = waitpid (-1, &status, 0)) > 0)
+ 	    if (!WIFEXITED(status))
+ 	      kill_thread (rpid);
+ 	}
+       else
+ 	while ((rpid = waitpid (inferior_pid, &status, 0)) > 0)
+ 	  if (!WIFEXITED(status))
+ 	    ptrace (PT_KILL, inferior_pid, (PTRACE_ARG3_TYPE) 0, 0);
+     }
+ #endif
+ 
+   /* Wait for all threads. */
+   do
+     rpid = waitpid (-1, &status, __WCLONE | WNOHANG);
+   while (rpid > 0 || errno == EINTR);
+ 
+   do
+     rpid = waitpid (-1, &status, WNOHANG);
+   while (rpid > 0 || errno == EINTR);
+ 
+   linuxthreads_mourn_inferior ();
+ }
+ 
+ /* Insert a breakpoint */
+ 
+ static int
+ linuxthreads_insert_breakpoint (addr, contents_cache)
+     CORE_ADDR addr;
+     char *contents_cache;
+ {
+   if (linuxthreads_max && linuxthreads_manager_pid != 0)
+     {
+       linuxthreads_breakpoint_addr = addr;
+       iterate_active_threads (insert_breakpoint, 1);
+       insert_breakpoint (linuxthreads_manager_pid);
+     }
+ 
+   return child_ops.to_insert_breakpoint (addr, contents_cache);
+ }
+ 
+ /* Remove a breakpoint */
+ 
+ static int
+ linuxthreads_remove_breakpoint (addr, contents_cache)
+     CORE_ADDR addr;
+     char *contents_cache;
+ {
+   if (linuxthreads_max && linuxthreads_manager_pid != 0)
+     {
+       linuxthreads_breakpoint_addr = addr;
+       iterate_active_threads (remove_breakpoint, 1);
+       remove_breakpoint (linuxthreads_manager_pid);
+     }
+ 
+   return child_ops.to_remove_breakpoint (addr, contents_cache);
+ }
+ 
+ /* Mark our target-struct as eligible for stray "run" and "attach" commands.  */
+ 
+ static int
+ linuxthreads_can_run ()
+ {
+   return child_suppress_run;
+ }
+ 
+ static void
+ init_linuxthreads_ops ()
+ {
+   linuxthreads_ops.to_shortname = "linuxthreads";
+   linuxthreads_ops.to_longname  = "LINUX threads and pthread.";
+   linuxthreads_ops.to_doc       = "LINUX threads and pthread support.";
+   linuxthreads_ops.to_attach    = linuxthreads_attach;
+   linuxthreads_ops.to_detach    = linuxthreads_detach;
+   linuxthreads_ops.to_resume    = linuxthreads_resume;
+   linuxthreads_ops.to_wait      = linuxthreads_wait;
+   linuxthreads_ops.to_kill      = linuxthreads_kill;
+   linuxthreads_ops.to_can_run   = linuxthreads_can_run;
+   linuxthreads_ops.to_stratum   = thread_stratum;
+   linuxthreads_ops.to_insert_breakpoint = linuxthreads_insert_breakpoint;
+   linuxthreads_ops.to_remove_breakpoint = linuxthreads_remove_breakpoint;
+   linuxthreads_ops.to_create_inferior   = linuxthreads_create_inferior;
+   linuxthreads_ops.to_mourn_inferior    = linuxthreads_mourn_inferior;
+   linuxthreads_ops.to_thread_alive      = linuxthreads_thread_alive;
+   linuxthreads_ops.to_magic             = OPS_MAGIC;
+ }
+ 
+ void
+ _initialize_linuxthreads ()
+ {
+   struct sigaction sact;
+ 
+   init_linuxthreads_ops ();
+   add_target (&linuxthreads_ops);
+   child_suppress_run = 1;
+ 
+   /* Attach SIGCHLD handler */
+   sact.sa_handler = sigchld_handler;
+   sigemptyset (&sact.sa_mask);
+   sact.sa_flags = 0;
+   sigaction (SIGCHLD, &sact, NULL);
+ 
+   /* initialize SIGCHLD mask */
+   sigemptyset (&linuxthreads_wait_mask);
+   sigaddset (&linuxthreads_wait_mask, SIGCHLD);
+ }
diff -Ncr /home/kingdon/work/gdb/gdb/target.c ./gdb/target.c
*** /home/kingdon/work/gdb/gdb/target.c	Thu Aug 12 11:13:38 1999
--- ./gdb/target.c	Sat Aug 14 19:49:45 1999
***************
*** 1238,1243 ****
--- 1238,1244 ----
    {"SIG61", "Real-time event 61"},
    {"SIG62", "Real-time event 62"},
    {"SIG63", "Real-time event 63"},
+   {"SIG32", "Real-time event 32"},
  
  #if defined(MACH) || defined(__MACH__)
    /* Mach exceptions */
***************
*** 1571,1578 ****
  
  #if defined (REALTIME_LO)
    if (hostsig >= REALTIME_LO && hostsig < REALTIME_HI)
!     return (enum target_signal)
!       (hostsig - 33 + (int) TARGET_SIGNAL_REALTIME_33);
  #endif
    return TARGET_SIGNAL_UNKNOWN;
  }
--- 1572,1587 ----
  
  #if defined (REALTIME_LO)
    if (hostsig >= REALTIME_LO && hostsig < REALTIME_HI)
!     {
!       /* This block of TARGET_SIGNAL_REALTIME value is in order.  */
!       if (33 <= hostsig && hostsig <= 63)
! 	return (enum target_signal)
! 	  (hostsig - 33 + (int) TARGET_SIGNAL_REALTIME_33);
!       else if (hostsig == 32)
! 	return TARGET_SIGNAL_REALTIME_32;
!       else
! 	error ("GDB bug: target.c (target_signal_from_host): unrecognized real-time signal");
!     }
  #endif
    return TARGET_SIGNAL_UNKNOWN;
  }
***************
*** 1619,1624 ****
--- 1628,1635 ----
      case TARGET_SIGNAL_PRIO:
        return SIGPRIO;
  #endif
+ 
+     case TARGET_SIGNAL_REALTIME_32: return 32; /* by definition */ 
  
        /* Mach exceptions.  Assumes that the values for EXC_ are positive! */
  #if defined (EXC_BAD_ACCESS) && defined (_NSIG)
diff -Ncr /home/kingdon/work/gdb/gdb/target.h ./gdb/target.h
*** /home/kingdon/work/gdb/gdb/target.h	Thu Aug 12 11:13:38 1999
--- ./gdb/target.h	Sat Aug 14 19:07:48 1999
***************
*** 48,54 ****
  	file_stratum,		/* Executable files, etc */
  	core_stratum,		/* Core dump files */
  	download_stratum,	/* Downloading of remote targets */
! 	process_stratum		/* Executing processes */
  };
  
  enum thread_control_capabilities {
--- 48,55 ----
  	file_stratum,		/* Executable files, etc */
  	core_stratum,		/* Core dump files */
  	download_stratum,	/* Downloading of remote targets */
! 	process_stratum,	/* Executing processes */
! 	thread_stratum		/* Executing threads */
  };
  
  enum thread_control_capabilities {
***************
*** 214,219 ****
--- 215,227 ----
    TARGET_EXC_SOFTWARE = 80,
    TARGET_EXC_BREAKPOINT = 81,
  #endif
+ 
+   /* Yes, this pains me, too.  But LynxOS didn't have SIG32, and now
+      Linux does, and we can't disturb the numbering, since it's part
+      of the protocol.  Note that in some GDB's TARGET_SIGNAL_REALTIME_32
+      is number 76.  */
+   TARGET_SIGNAL_REALTIME_32,
+ 
    /* Some signal we don't know about.  */
    TARGET_SIGNAL_UNKNOWN,
  
