diff -ru gdb-4.17-ORIG/gdb/breakpoint.c gdb-4.17/gdb/breakpoint.c
--- gdb-4.17-ORIG/gdb/breakpoint.c	Thu Apr  9 01:51:56 1998
+++ gdb-4.17/gdb/breakpoint.c	Tue May  5 18:36:51 1998
@@ -1,6 +1,6 @@
 /* Everything about breakpoints, for GDB.
-   Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
-             Free Software Foundation, Inc.
+   Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
+   1998 Free Software Foundation, Inc.
 
 This file is part of GDB.
 
@@ -2165,12 +2165,19 @@
       struct minimal_symbol *m;
 
       m = lookup_minimal_symbol_text (func_name, NULL, (struct objfile *)NULL);
-      if (m)
-	sal.pc = SYMBOL_VALUE_ADDRESS (m);
-      else
+      if (!m)
 	return;
+
+      sal.pc = SYMBOL_VALUE_ADDRESS (m);
+      sal.section = find_pc_overlay (sal.pc);
+
+      /* Don't insert twice a bp_longjmp breakpoint at the same address */
+      ALL_BREAKPOINTS (b)
+	if (b->type == bp_longjmp
+	    && b->address == sal.pc
+	    && (overlay_debugging == 0 || b->section == sal.section))
+	  return;
     }
-  sal.section = find_pc_overlay (sal.pc);
   b = set_raw_breakpoint (sal);
   if (!b) return;
 
@@ -3675,6 +3682,7 @@
 #ifdef GET_LONGJMP_TARGET
   create_longjmp_breakpoint ("longjmp");
   create_longjmp_breakpoint ("_longjmp");
+  create_longjmp_breakpoint ("__longjmp");
   create_longjmp_breakpoint ("siglongjmp");
   create_longjmp_breakpoint ("_siglongjmp");
   create_longjmp_breakpoint (NULL);
diff -ru gdb-4.17-ORIG/gdb/config/i386/linux.mh gdb-4.17/gdb/config/i386/linux.mh
--- gdb-4.17-ORIG/gdb/config/i386/linux.mh	Wed Apr 22 03:23:13 1998
+++ gdb-4.17/gdb/config/i386/linux.mh	Thu Apr 30 18:11:25 1998
@@ -5,4 +5,4 @@
 
 NAT_FILE= nm-linux.h
 NATDEPFILES= infptrace.o solib.o inftarg.o fork-child.o corelow.o \
-  core-aout.o core-regset.o i386lnx-nat.o
+  core-aout.o core-regset.o i386lnx-nat.o linuxthreads.o
diff -ru gdb-4.17-ORIG/gdb/config/i386/linux.mt gdb-4.17/gdb/config/i386/linux.mt
--- gdb-4.17-ORIG/gdb/config/i386/linux.mt	Wed Apr 22 03:23:15 1998
+++ gdb-4.17/gdb/config/i386/linux.mt	Thu Apr 30 19:53:53 1998
@@ -2,4 +2,7 @@
 TDEPFILES= i386-tdep.o i387-tdep.o
 TM_FILE= tm-linux.h
 
+# The following define is used to get the JB_PC #define from <jmp_buf.h>
+MT_CFLAGS= -D__USE_MISC
+
 GDBSERVER_DEPFILES= low-linux.o
diff -ru gdb-4.17-ORIG/gdb/config/i386/nm-linux.h gdb-4.17/gdb/config/i386/nm-linux.h
--- gdb-4.17-ORIG/gdb/config/i386/nm-linux.h	Wed Apr 22 03:23:16 1998
+++ gdb-4.17/gdb/config/i386/nm-linux.h	Thu Apr 30 18:20:06 1998
@@ -74,4 +74,22 @@
 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(STEP) linuxthreads_prepare_to_proceed (STEP)
+
 #endif /* #ifndef NM_LINUX_H */
diff -ru gdb-4.17-ORIG/gdb/config/i386/tm-i386.h gdb-4.17/gdb/config/i386/tm-i386.h
--- gdb-4.17-ORIG/gdb/config/i386/tm-i386.h	Thu Jan  4 08:23:24 1996
+++ gdb-4.17/gdb/config/i386/tm-i386.h	Tue Jun  9 17:27:39 1998
@@ -198,12 +198,9 @@
    In the case of the i386, the frame's nominal address
    is the address of a 4-byte word containing the calling frame's address.  */
 
-#define FRAME_CHAIN(thisframe)  \
-  ((thisframe)->signal_handler_caller \
-   ? (thisframe)->frame \
-   : (!inside_entry_file ((thisframe)->pc) \
-      ? read_memory_integer ((thisframe)->frame, 4) \
-      : 0))
+extern CORE_ADDR i386_frame_chain PARAMS ((struct frame_info *));
+
+#define FRAME_CHAIN(FRAME) (i386_frame_chain (FRAME))
 
 /* A macro that tells us whether the function invocation represented
    by FI does not have a frame on the stack associated with it.  If it
diff -ru gdb-4.17-ORIG/gdb/config/i386/tm-linux.h gdb-4.17/gdb/config/i386/tm-linux.h
--- gdb-4.17-ORIG/gdb/config/i386/tm-linux.h	Wed Apr 22 03:23:17 1998
+++ gdb-4.17/gdb/config/i386/tm-linux.h	Tue May  5 16:38:28 1998
@@ -1,5 +1,5 @@
 /* Definitions to target GDB to GNU/Linux on 386.
-   Copyright 1992, 1993 Free Software Foundation, Inc.
+   Copyright 1992, 1993, 1998 Free Software Foundation, Inc.
 
 This file is part of GDB.
 
@@ -25,8 +25,75 @@
 
 #include "i386/tm-i386.h"
 
-/* Offset to saved PC in sigcontext, from <linux/signal.h>.  */
-#define SIGCONTEXT_PC_OFFSET 38
+/* Size of an element of the jmp_buf. */
+
+#define JB_ELEMENT_SIZE sizeof (int)
+
+/* Figure out where the longjmp will land.  Slurp the args out of the stack.
+   We expect the first arg to be a pointer to the jmp_buf structure from which
+   we extract the pc (JB_PC) that we will land at.  The pc is copied into ADDR.
+   This routine returns true on success */
+
+extern int
+get_longjmp_target PARAMS ((CORE_ADDR *));
+#define GET_LONGJMP_TARGET(ADDR) get_longjmp_target(ADDR)
+
+/* Offset to saved PC and EFLAGS in sigcontext, from <linux/signal.h>.  */
+#define SIGCONTEXT_PC_OFFSET (14 * 4)
+#define SIGCONTEXT_EFLAGS_OFFSET (16 * 4)
+
+/* Size of sigcontext, from <linux/signal.h>.  */
+#define SIGCONTEXT_SIZE (22 * 4)
+
+/* Address of sigcontext given the sigtramp frame */
+
+#define SIGCONTEXT_ADDR(frame) (SIGTRAMP_START((frame)->pc) - SIGCONTEXT_SIZE)
+
+/* Are we currently handling a signal ?  */
+
+extern int i386_linux_sigtramp_offset PARAMS ((CORE_ADDR));
+#undef IN_SIGTRAMP
+#define IN_SIGTRAMP(pc, name)   (i386_linux_sigtramp_offset (pc) >= 0)
+
+/* Get start and end address of sigtramp handler.  */
+
+#define SIGTRAMP_START(pc)      ((pc) - i386_linux_sigtramp_offset (pc))
+#define SIGTRAMP_END(pc)        (SIGTRAMP_START(pc) + 8)
+
+/* Determine whether a pc is the first instruction of a signal handler.  */
+
+#define START_SIGHANDLER(pc, func_start, func_name) \
+  ((pc) == (func_start) && i386_linux_sigtramp_offset (read_sp()) == 0)
+
+/* Need to redefine child_resume for the step/next action in signal handlers */
+
+#define	CHILD_RESUME
+
+/* If PC contains this instruction, then we know that next instruction
+   will be a system call.  */
+
+#define SYSCALL_TRAP 0xcd80	/* int $0x80 */
+#define SYSCALL_TRAP_SIZE 2	/* SYSCALL_TRAP instruction size */
+
+/* Immediately after a function call, return the saved pc.  Can't always go
+   through the frames for this because on some machines the new frame is not
+   set up until the new function executes some instructions.  */
+
+#undef SAVED_PC_AFTER_CALL
+#define SAVED_PC_AFTER_CALL(_frame) \
+  (read_memory_integer (read_register (SP_REGNUM) \
+			+ ((_frame)->signal_handler_caller \
+			   ? SIGCONTEXT_PC_OFFSET + 4 : 0), 4)) 
+
+/* Saved PC.  Get it from sigcontext if within sigtramp.  */
+
+extern CORE_ADDR i386_linux_sigtramp_saved_pc PARAMS ((struct frame_info *));
+
+#undef FRAME_SAVED_PC
+#define FRAME_SAVED_PC(_frame) \
+  (((_frame)->signal_handler_caller \
+    ? i386_linux_sigtramp_saved_pc (_frame) \
+    : read_memory_integer ((_frame)->frame + 4, 4)))
 
 /* We need this file for the SOLIB_TRAMPOLINE stuff. */
 
@@ -34,5 +101,9 @@
 
 /* The following works around a problem with /usr/include/sys/procfs.h  */
 #define sys_quotactl 1
+
+/* The compiler/loader puts out 0 instead of the address in N_SO symbols,
+   and in N_FUN symbols too.  */
+#define SOFUN_ADDRESS_MAYBE_MISSING
 
 #endif  /* #ifndef TM_LINUX_H */
diff -ru gdb-4.17-ORIG/gdb/config/nm-m3.h gdb-4.17/gdb/config/nm-m3.h
--- gdb-4.17-ORIG/gdb/config/nm-m3.h	Fri Apr 12 08:15:16 1996
+++ gdb-4.17/gdb/config/nm-m3.h	Thu Apr 30 18:21:07 1998
@@ -1,6 +1,6 @@
 /* Mach 3.0 common definitions and global vars.
 
-   Copyright (C) 1992 Free Software Foundation, Inc.
+   Copyright (C) 1992, 1998 Free Software Foundation, Inc.
 
 This file is part of GDB.
 
@@ -39,7 +39,7 @@
  */
 extern int must_suspend_thread;
 
-#define PREPARE_TO_PROCEED(select_it) mach3_prepare_to_proceed(select_it)
+#define PREPARE_TO_PROCEED(step) mach3_prepare_to_proceed(step)
 
 /* Try to get the privileged host port for authentication to machid
  *
diff -ru gdb-4.17-ORIG/gdb/i386-tdep.c gdb-4.17/gdb/i386-tdep.c
--- gdb-4.17-ORIG/gdb/i386-tdep.c	Sat Apr 11 07:39:37 1998
+++ gdb-4.17/gdb/i386-tdep.c	Tue Jun  9 17:31:04 1998
@@ -575,6 +575,7 @@
 }
 
 #ifdef GET_LONGJMP_TARGET
+#include <setjmp.h>
 
 /* Figure out where the longjmp will land.  Slurp the args out of the stack.
    We expect the first arg to be a pointer to the jmp_buf structure from which
@@ -712,6 +713,156 @@
   return 0;			/* not a trampoline */
 }
 
+/* i386_frame_chain() takes a frame's nominal address and produces the frame's
+   chain-pointer.  In the case of the i386, the frame's nominal address is
+   the address of a 4-byte word containing the calling frame's address.  */
+
+CORE_ADDR
+i386_frame_chain (frame)
+     struct frame_info *frame;
+{
+  char buf[4];
+
+  if (frame->signal_handler_caller)
+    return frame->frame;
+
+  if (!inside_entry_file (frame->pc) &&
+      target_read_memory (frame->frame, buf, 4) == 0)
+    return extract_address (buf, 4);
+
+  return 0;
+}
+
+/* Under Linux, signal handler invocations can be identified by the
+   designated code sequence that is used to return from a signal
+   handler.  In particular, the return address of a signal handler
+   points to the following sequence:
+
+	0x58		popl %eax
+	0xb877000000	movl $0x77,%eax
+	0xcd80		int  $0x80
+
+   Each instruction has a unique encoding, so we simply attempt to
+   match the instruction the pc is pointing to with any of the above
+   instructions.  If there is a hit, we know the offset to the start
+   of the designated sequence and can then check whether we really are
+   executing in a designated sequence.  If not, -1 is returned,
+   otherwise the offset from the start of the desingated sequence is
+   returned.
+
+   There is a slight chance of false hits: code could jump into the
+   middle of the designated sequence, in which case there is no
+   guarantee that we are in the middle of a sigreturn syscall.  Don't
+   think this will be a problem in praxis, though.
+*/
+int
+i386_linux_sigtramp_offset (pc)
+     CORE_ADDR pc;
+{
+  unsigned char code[8];
+  unsigned char sigtramp[] = { 0x58, 0xb8, 0x77, 0x00, 0x00, 0x00, 0xcd, 0x80 };
+  int off, i;
+
+  if (read_memory_nobpt(pc, (char *) code, 1) != 0)
+    return -1;
+
+  switch (code[0])
+  {
+    case 0x58:	off = 0; break;	/* popl %eax */
+    case 0xb8:	off = 1; break;	/* movl $0x77,%eax */
+    case 0xcd:	off = 6; break;	/* int  $0x80 */
+    default:	return -1;
+  }
+  pc -= off;
+
+  for (i = 0; i < sizeof (code); i++)
+    if (read_memory_nobpt(pc + i, (char *) &code[i], 1) != 0)
+      return -1;
+
+  return memcmp (sigtramp, code, sizeof (code)) == 0 ? off : -1;
+}
+
+/* Get saved user PC for sigtramp from sigcontext for Linux style sigtramp.  */
+
+CORE_ADDR
+i386_linux_sigtramp_saved_pc (frame)
+     struct frame_info *frame;
+{
+  char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT];
+  int ptrbytes = TARGET_PTR_BIT / TARGET_CHAR_BIT;
+
+  /* Don't cause a memory_error when accessing sigcontext in case the stack
+     layout has changed or the stack is corrupt.  */
+  target_read_memory (SIGCONTEXT_ADDR (frame) + SIGCONTEXT_PC_OFFSET,
+		      buf, ptrbytes);
+  return extract_unsigned_integer (buf, ptrbytes);
+}
+
+#ifdef CHILD_RESUME
+
+#include <sys/ptrace.h>
+#ifndef PT_SYSCALL
+#define PT_SYSCALL PTRACE_SYSCALL
+#endif
+
+void
+child_resume(pid, step, signal)
+     int pid;
+     int step;
+     enum target_signal signal;
+{
+  int request;
+  unsigned char code;
+  CORE_ADDR pc;
+  int i;
+
+  errno = 0;
+
+  if (pid == -1)
+    /* Resume all threads.  */
+    /* I think this only gets used in the non-threaded case, where "resume
+	all threads" and "resume inferior_pid" are the same.  */
+    pid = inferior_pid;
+
+  if (!step)
+    request = PT_CONTINUE;
+  else
+    {
+      pc = read_pc_pid (pid);
+      for (i = 0; i < SYSCALL_TRAP_SIZE; i++)
+	if (read_memory_nobpt(pc + i, (char *) &code, 1) != 0
+	    || code != ((SYSCALL_TRAP >> ((SYSCALL_TRAP_SIZE - 1 - i) * 8))
+			& 0xFF))
+	  break;
+
+      if (i < SYSCALL_TRAP_SIZE)
+	request = PT_STEP;
+      else if (!IN_SIGTRAMP (pc, (char *)NULL))
+	{
+	  /* Single-step over the syscall in order to avoid being blocked
+	     inside the kernel waiting for the thread to be unblocked.  */
+	  request = PT_SYSCALL;
+	}
+      else
+	{
+	  /* Put TF in the eflags from the frame set up by the signal handler */
+	  unsigned long eflags;
+	  CORE_ADDR addr = read_sp () + SIGCONTEXT_EFLAGS_OFFSET;
+	  if (target_read_memory (addr, (char *) &eflags, 4) == 0)
+	    {
+	      eflags |= 0x100; /* Trap Flag */
+	      write_memory (addr, (char *) &eflags, 4);
+	    }
+	  request = PT_STEP;
+	}
+    }
+  call_ptrace (request, pid, (PTRACE_ARG3_TYPE) 0,
+	       target_signal_to_host (signal));
+
+  if (errno)
+    perror_with_name ("ptrace");
+}
+#endif
 
 void
 _initialize_i386_tdep ()
diff -ru gdb-4.17-ORIG/gdb/inferior.h gdb-4.17/gdb/inferior.h
--- gdb-4.17-ORIG/gdb/inferior.h	Sat Apr 11 07:39:38 1998
+++ gdb-4.17/gdb/inferior.h	Thu Apr 30 18:28:52 1998
@@ -213,6 +213,12 @@
 
 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 -ru gdb-4.17-ORIG/gdb/infrun.c gdb-4.17/gdb/infrun.c
--- gdb-4.17-ORIG/gdb/infrun.c	Sat Apr 11 07:39:39 1998
+++ gdb-4.17/gdb/infrun.c	Thu Apr 30 18:43:37 1998
@@ -59,6 +59,14 @@
 #define GET_LONGJMP_TARGET(PC_ADDR) 0
 #endif
 
+/* Determine whether a pc is pointing to the first instruction of a signal
+   handler.  This can be difficult to compute on some systems (like Linux)
+   where the sigreturn trampoline is only used on return and not on call.  */
+
+#ifndef START_SIGHANDLER
+#define START_SIGHANDLER(pc, func_start, func_name) \
+  ((pc) == (func_start) && IN_SIGTRAMP((pc), (func_name)))
+#endif
 
 /* Some machines have trampoline code that sits between function callers
    and the actual functions themselves.  If this machine doesn't have
@@ -210,6 +218,14 @@
 
 static int stop_print_frame;
 
+#ifdef PREPARE_TO_PROCEED
+/* When a pid must be single-stepped for going over a breakpoint at
+   proceed (), it should be implicitely stepped by target_resume() in
+   resume (), implicitely waited for in target_wait() and switched to
+   in wait_for_inferior().  */
+
+static int proceeded_pid;
+#endif /* PREPARE_TO_PROCEED */
 
 /* Things to clean up if we QUIT out of resume ().  */
 /* ARGSUSED */
@@ -259,7 +275,7 @@
   /* Install inferior's terminal modes.  */
   target_terminal_inferior ();
 
-  target_resume (-1, step, sig);
+  target_resume (step && !breakpoints_inserted ? inferior_pid : -1, step, sig);
   discard_cleanups (old_cleanups);
 }
 
@@ -336,15 +352,9 @@
      In this case the thread that stopped at a breakpoint will immediately
      cause another stop, if it is not stepped over first. On the other hand,
      if (ADDR != -1) we only want to single step over the breakpoint if we did
-     switch to another thread.
-
-     If we are single stepping, don't do any of the above.
-     (Note that in the current implementation single stepping another
-     thread after a breakpoint and then continuing will cause the original
-     breakpoint to be hit again, but you can always continue, so it's not
-     a big deal.)  */
+     switch to another thread.  */
 
-  if (! step && PREPARE_TO_PROCEED (1) && breakpoint_here_p (read_pc ()))
+  if (!oneproc && (proceeded_pid = PREPARE_TO_PROCEED (step)))
     oneproc = 1;
 #endif /* PREPARE_TO_PROCEED */
 
@@ -440,6 +450,10 @@
 
   /* Don't confuse first call to proceed(). */
   stop_signal = TARGET_SIGNAL_0;
+
+#ifdef PREPARE_TO_PROCEED
+  proceeded_pid = 0;
+#endif
 }
 
 static void
@@ -511,9 +525,47 @@
       registers_changed ();
 
       if (target_wait_hook)
-	pid = target_wait_hook (-1, &w);
+	pid = target_wait_hook (!breakpoints_inserted ? inferior_pid : -1, &w);
       else
-	pid = target_wait (-1, &w);
+	pid = target_wait (!breakpoints_inserted ? inferior_pid : -1, &w);
+
+#ifdef PREPARE_TO_PROCEED
+      /* Switch to the thread selected by the last PREPARE_TO_PROCEED ().
+	 As a side effect, the trap_expected value should be switched.  */
+
+      if (proceeded_pid)
+	{
+	  if (proceeded_pid != inferior_pid)
+	    {
+	      trap_expected = 0;
+
+	      /* Save infrun state for the old thread.  */
+	      save_infrun_state (inferior_pid, prev_pc,
+				 prev_func_start, prev_func_name,
+				 trap_expected, step_resume_breakpoint,
+				 through_sigtramp_breakpoint,
+				 step_range_start, step_range_end,
+				 step_frame_address, handling_longjmp,
+				 another_trap);
+
+	      inferior_pid = proceeded_pid;
+
+	      /* Load infrun state for the new thread.  */
+	      load_infrun_state (inferior_pid, &prev_pc,
+				 &prev_func_start, &prev_func_name,
+				 &trap_expected, &step_resume_breakpoint,
+				 &through_sigtramp_breakpoint,
+				 &step_range_start, &step_range_end,
+				 &step_frame_address, &handling_longjmp,
+				 &another_trap);
+	      printf_filtered ("[Switching to %s]\n",
+			       target_pid_to_str (inferior_pid));
+
+	      trap_expected = 1;
+	    }
+	  proceeded_pid = 0;
+	}
+#endif /* PREPARE_TO_PROCEED */
 
     /* Gross.
 
@@ -669,8 +721,8 @@
 
 		    remove_breakpoints ();
 		    target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */
-		    /* FIXME: What if a signal arrives instead of the single-step
-		       happening?  */
+		    /* FIXME: What if a signal arrives instead of the
+		       single-step happening?  */
 
 		    if (target_wait_hook)
 		      target_wait_hook (pid, &w);
@@ -678,6 +730,9 @@
 		      target_wait (pid, &w);
 		    insert_breakpoints ();
 
+		    if (inferior_pid == pid && CURRENTLY_STEPPING())
+		      goto have_waited;
+
 		    /* We need to restart all the threads now.  */
 		    target_resume (-1, 0, TARGET_SIGNAL_0);
 		    continue;
@@ -777,7 +832,6 @@
 	  else
 	    target_wait (pid, &tmpstatus);
 
-
 	  goto have_waited;
 	}
 
@@ -1239,9 +1293,7 @@
       update_step_sp = 1;
 
       /* Did we just take a signal?  */
-      if (IN_SIGTRAMP (stop_pc, stop_func_name)
-	  && !IN_SIGTRAMP (prev_pc, prev_func_name)
-	  && read_sp () INNER_THAN step_sp)
+      if (START_SIGHANDLER (stop_pc, stop_func_start, stop_func_name))
 	{
 	  /* We've just taken a signal; go until we are back to
 	     the point where we took it and one more.  */
@@ -1432,8 +1484,19 @@
 	    step_resume_breakpoint =
 	      set_momentary_breakpoint (sr_sal, get_current_frame (),
 					bp_step_resume);
-	    if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
+	    if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc)
+		&& !IN_SIGTRAMP (stop_pc, NULL))
 	      step_resume_breakpoint->frame = step_frame_address;
+
+	    if (sr_sal.pc < step_range_start
+		|| sr_sal.pc >= step_range_end)
+	      {
+		/* If the return function is out_of stepping range, then
+		   update stepping range to match the return address. This
+		   code deals with jumps to functions that did not return
+		   to the current function.  */
+		step_range_start = step_range_end = sr_sal.pc;
+	      }
 	    if (breakpoints_inserted)
 	      insert_breakpoints ();
 	  }
@@ -1583,9 +1646,7 @@
 
     check_sigtramp2:
       if (trap_expected
-	  && IN_SIGTRAMP (stop_pc, stop_func_name)
-	  && !IN_SIGTRAMP (prev_pc, prev_func_name)
-	  && read_sp () INNER_THAN step_sp)
+	  && START_SIGHANDLER (stop_pc, stop_func_start, stop_func_name))
 	{
 	  /* What has happened here is that we have just stepped the inferior
 	     with a signal (because it is a signal which shouldn't make
@@ -1847,6 +1908,33 @@
      int signo;
 {
   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
diff -ru gdb-4.17-ORIG/gdb/m3-nat.c gdb-4.17/gdb/m3-nat.c
--- gdb-4.17-ORIG/gdb/m3-nat.c	Mon May  6 23:27:04 1996
+++ gdb-4.17/gdb/m3-nat.c	Thu Apr 30 18:23:57 1998
@@ -1,7 +1,7 @@
 /* Interface GDB to Mach 3.0 operating systems.
    (Most) Mach 3.0 related routines live in this file.
 
-   Copyright (C) 1992, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1992, 1996, 1998 Free Software Foundation, Inc.
 
 This file is part of GDB.
 
@@ -1576,26 +1576,23 @@
  *
  * If we have switched threads and stopped at breakpoint return 1 otherwise 0.
  *
- *  if SELECT_IT is nonzero, reselect the thread that was active when
- *  we stopped at a breakpoint.
- *
+ * If we are single stepping, don't do anything since in the current
+ * implementation single stepping another thread after a breakpoint and
+ * then continuing will cause the original breakpoint to be hit again,
+ * but you can always continue, so it's not a big deal.
  */
 
-mach3_prepare_to_proceed (select_it)
-     int select_it;
+mach3_prepare_to_proceed (step)
+     int step;
 {
-  if (stop_thread &&
+  if (!step &&
+      stop_thread &&
       stop_thread != current_thread &&
       stop_exception == EXC_BREAKPOINT)
     {
-      int mid;
-
-      if (! select_it)
-	return 1;
-
-      mid = switch_to_thread (stop_thread);
-
-      return 1;
+      switch_to_thread (stop_thread);
+      if (breakpoint_here_p (read_pc ()))
+	return inferior_pid;
     }
 
   return 0;
diff -ru gdb-4.17-ORIG/gdb/target.h gdb-4.17/gdb/target.h
--- gdb-4.17-ORIG/gdb/target.h	Thu May  8 03:00:40 1997
+++ gdb-4.17/gdb/target.h	Thu Apr 30 18:31:41 1998
@@ -1,5 +1,5 @@
 /* Interface between GDB and target environments, including files and processes
-   Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+   Copyright 1990, 1991, 1992, 1993, 1994, 1998 Free Software Foundation, Inc.
    Contributed by Cygnus Support.  Written by John Gilmore.
 
 This file is part of GDB.
@@ -47,7 +47,8 @@
 	file_stratum,		/* Executable files, etc */
 	core_stratum,		/* Core dump files */
 	download_stratum,	/* Downloading of remote targets */
-	process_stratum		/* Executing processes */
+	process_stratum,	/* Executing processes */
+	thread_stratum		/* Executing threads */
 };
 
 /* Stuff for target_wait.  */
--- /dev/null	Tue Jan  1 05:00:00 1980
+++ gdb-4.17/gdb/linuxthreads.c	Thu Apr 30 18:31:03 1998
@@ -0,0 +1,1359 @@
+/* Low level interface for debugging GNU/Linux threads for GDB,
+   the GNU debugger.
+   Copyright 1998 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 "defs.h"
+#include "target.h"
+#include "inferior.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "wait.h"
+
+#include "breakpoint.h"
+
+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 */
+
+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 */
+
+static int *linuxthreads_wait_pid;	/* wait array of pid */
+static int *linuxthreads_wait_status;	/* wait array of status */
+static int linuxthreads_wait_last;	/* last status to be reported */
+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 */
+
+static int linuxthreads_sig_restart;		/* SIG_RESTART target value */
+static int linuxthreads_sig_restart_stop;	/* SIG_RESTART stop */
+static int linuxthreads_sig_restart_print;	/* SIG_RESTART print */
+
+static int linuxthreads_sig_cancel;		/* SIG_CANCEL target value */
+static int linuxthreads_sig_cancel_stop;	/* SIG_CANCEL stop */
+static int linuxthreads_sig_cancel_print;	/* SIG_CANCEL print */
+
+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 */
+static CORE_ADDR linuxthreads_breakpoint_addr;	/* Zombie breapoint address */
+
+#define	REMOVE_BREAKPOINT_ZOMBIE(_i) \
+{ \
+  if ((_i) < linuxthreads_breakpoint_last) \
+    linuxthreads_breakpoint_zombie[(_i)] = \
+      linuxthreads_breakpoint_zombie[linuxthreads_breakpoint_last]; \
+  linuxthreads_breakpoint_last--; \
+}
+
+/* This should be part of the linuxthreads package */
+#define LINUXTHREAD_SIG_CANCEL		12 /* SIGUSR2 */
+#define LINUXTHREAD_SIG_EXIT		10 /* SIGUSR1 */
+#define LINUXTHREAD_NSIG		_NSIG
+
+
+#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 and optionally a SIGSTOP one.  */
+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;
+  int last = 0;
+  int *wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int));
+
+  /* Look at the pending status */
+  for (i = linuxthreads_wait_last; i >= 0; i--)
+    if (linuxthreads_wait_pid[i] == pid)
+      {
+	status = linuxthreads_wait_status[i];
+	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)
+	  {
+	    wstatus[0] = status;
+	    last = 1;
+	  }
+	else if (stop)
+	    found_stop = 1;
+
+	break;
+      }
+
+  if (stop)
+    {
+      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;
+
+	  /* manager has died or pid is initial thread.  */
+	  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 all signals to the thread */
+  while (--last >= 0)
+    kill (pid, WSTOPSIG(wstatus[last]));
+
+  return 1;
+}
+
+static void
+restore_inferior_pid (pid)
+    int pid;
+{
+  inferior_pid = pid;
+}
+
+static struct cleanup *
+save_inferior_pid ()
+{
+  return make_cleanup (restore_inferior_pid, inferior_pid);
+}
+
+/* SIGCHLD handler */
+static void
+sigchld_handler(signo)
+    int signo;
+{
+    /* This handler is used to get an EINTR while doing waitpid()
+       when an event is received */
+}
+
+/* Does the process currently have a pending status ? */
+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;
+}
+
+/* Walk through the linuxthreads handles in order to execute a function */
+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 && (all || (!linuxthreads_pending_status (pid))))
+	    (*func)(pid);
+	}
+    }
+
+}
+
+/* Insert a thread breakpoint */
+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;
+      }
+}
+
+/* Remove a thread breakpoint */
+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);
+      }
+}
+
+/* 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;
+
+	  /* manager has died or pid is initial thread.  */
+	  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;
+
+  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);
+		}
+	    }
+	  iterate_active_threads (stop_thread, 0);
+	  iterate_active_threads (wait_thread, 0);
+	}
+    }
+
+  if (old_chain != NULL)
+    do_cleanups (old_chain);
+}
+
+/* Internal linuxthreads signal management */
+
+static void
+linuxthreads_signal_update (on)
+    int on;
+{
+  int sig_restart = target_signal_from_host(linuxthreads_sig_restart);
+  int sig_cancel = target_signal_from_host(linuxthreads_sig_cancel);
+
+  if (on)
+    {
+      linuxthreads_sig_restart_stop = signal_stop_update(sig_restart, 0);
+      linuxthreads_sig_restart_print = signal_print_update(sig_restart, 0);
+      if (linuxthreads_sig_restart_stop != 1 ||
+	  linuxthreads_sig_restart_print != 1)
+	fprintf_unfiltered (gdb_stderr,
+			    "Linux thread target has modified %s handling\n",
+			    target_signal_to_string(sig_restart));
+
+      linuxthreads_sig_cancel_stop = signal_stop_update(sig_cancel, 0);
+      linuxthreads_sig_cancel_print = signal_print_update(sig_cancel, 0);
+      if (linuxthreads_sig_cancel_stop != 1 ||
+	  linuxthreads_sig_cancel_print != 1)
+	fprintf_unfiltered (gdb_stderr,
+			    "Linux thread target has modified %s handling\n",
+			    target_signal_to_string(sig_cancel));
+    }
+  else
+    {
+      signal_stop_update(sig_restart, linuxthreads_sig_restart_stop);
+      signal_print_update(sig_restart, linuxthreads_sig_restart_print);
+      if (linuxthreads_sig_restart_stop != 1 ||
+	  linuxthreads_sig_restart_print != 1)
+	fprintf_unfiltered (gdb_stderr,
+			    "Linux thread target has restored %s handling\n",
+			    target_signal_to_string(sig_restart));
+
+      signal_stop_update(sig_cancel, linuxthreads_sig_cancel_stop);
+      signal_print_update(sig_cancel, linuxthreads_sig_cancel_print);
+      if (linuxthreads_sig_cancel_stop != 1 ||
+	  linuxthreads_sig_cancel_print != 1)
+	fprintf_unfiltered (gdb_stderr,
+			    "Linux thread target has restored %s handling\n",
+			    target_signal_to_string(sig_cancel));
+    }
+}
+
+/* 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;
+  struct sigaction sact;
+
+  if (!objfile || linuxthreads_max)
+    return;
+
+  if ((ms = lookup_minimal_symbol ("__pthread_threads_debug",
+				   NULL, objfile)) == NULL)
+    {
+      /* The debugging-aware libpthreads is not present in this objfile */
+      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 ((ms = lookup_minimal_symbol ("__pthread_sig_restart",
+				   NULL, objfile)) == NULL
+      || target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
+			     (char *)&linuxthreads_sig_restart,
+			     sizeof (linuxthreads_sig_restart)) != 0)
+    {
+      fprintf_unfiltered (gdb_stderr,
+			  "Unable to find linuxthreads symbol \"%s\"\n",
+			  "__pthread_sig_restart");
+      return;
+    }
+	 
+  if ((ms = lookup_minimal_symbol ("__pthread_sig_cancel",
+				   NULL, objfile)) == NULL
+      || target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
+			     (char *)&linuxthreads_sig_cancel,
+			     sizeof (linuxthreads_sig_cancel)) != 0)
+    {
+      fprintf_unfiltered (gdb_stderr,
+			  "Unable to find linuxthreads symbol \"%s\"\n",
+			  "__pthread_sig_cancel");
+      return;
+    }
+
+  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;
+    }
+
+  /* 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;
+
+  /* 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));
+
+  /* handle linuxthread exit */
+  sact.sa_handler = sigchld_handler;
+  sigemptyset(&sact.sa_mask);
+  sact.sa_flags = 0;
+  sigaction(linuxthreads_sig_restart, &sact, NULL);
+
+  if (inferior_pid && !linuxthreads_attach_pending)
+    {
+      int on = 1;
+      target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on));
+      linuxthreads_attach_pending = 1;
+      linuxthreads_signal_update (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", linuxthreads_max ? "Thread" : "Pid", pid);
+
+  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;
+{
+  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;
+      linuxthreads_signal_update (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 (pid, ourstatus)
+    int pid;
+    struct target_waitstatus *ourstatus;
+{
+  int status;
+  int rpid;
+  int i;
+  int last;
+  int *wstatus;
+
+  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;
+	  wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int));
+	}
+      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_signal_update (1);
+	  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;
+{
+  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 */
+      linuxthreads_signal_update (0);
+    }
+
+  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 (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);
+
+  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;
+}
+
+struct target_ops linuxthreads_ops = {
+  "linuxthreads",			/* to_shortname */
+  "LINUX threads and pthread.", 	/* to_longname */
+  "LINUX threads and pthread support.",	/* to_doc */
+  0,					/* to_open */
+  0,					/* to_close */
+  linuxthreads_attach,			/* to_attach */
+  linuxthreads_detach, 			/* to_detach */
+  linuxthreads_resume,			/* to_resume */
+  linuxthreads_wait,			/* to_wait */
+  0,					/* to_fetch_registers */
+  0,					/* to_store_registers */
+  0,					/* to_prepare_to_store */
+  0,					/* to_xfer_memory */
+  0,					/* to_files_info */
+  linuxthreads_insert_breakpoint,	/* to_insert_breakpoint */
+  linuxthreads_remove_breakpoint,	/* to_remove_breakpoint */
+  0,					/* to_terminal_init */
+  0, 					/* to_terminal_inferior */
+  0,					/* to_terminal_ours_for_output */
+  0,					/* to_terminal_ours */
+  0,					/* to_terminal_info */
+  linuxthreads_kill,			/* to_kill */
+  0,					/* to_load */
+  0,					/* to_lookup_symbol */
+  linuxthreads_create_inferior,		/* to_create_inferior */
+  linuxthreads_mourn_inferior,		/* to_mourn_inferior */
+  linuxthreads_can_run,			/* to_can_run */
+  0,					/* to_notice_signals */
+  linuxthreads_thread_alive,		/* to_thread_alive */
+  0,					/* to_stop */
+  thread_stratum,			/* to_stratum */
+  0,					/* to_next */
+  0,					/* to_has_all_memory */
+  0,					/* to_has_memory */
+  1,					/* to_has_stack */
+  1,					/* to_has_registers */
+  1,					/* to_has_execution */
+  0,					/* sections */
+  0,					/* sections_end */
+  OPS_MAGIC				/* to_magic */
+};
+
+void
+_initialize_linuxthreads ()
+{
+  struct sigaction sact;
+
+  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);
+}
