OSDN Git Service

Initial revision
authorjsm <jsm>
Wed, 22 Dec 1999 21:45:03 +0000 (21:45 +0000)
committerjsm <jsm>
Wed, 22 Dec 1999 21:45:03 +0000 (21:45 +0000)
gdb/arm-linux-nat.c [new file with mode: 0644]
gdb/config/arm/embed.mt [new file with mode: 0644]
gdb/config/arm/linux.mh [new file with mode: 0644]
gdb/config/arm/linux.mt [new file with mode: 0644]
gdb/config/arm/nm-linux.h [new file with mode: 0644]
gdb/config/arm/tm-embed.h [new file with mode: 0644]
gdb/config/arm/tm-linux.h [new file with mode: 0644]
gdb/config/arm/xm-linux.h [new file with mode: 0644]
gdb/gdb_proc_service.h [new file with mode: 0644]
gdb/gdb_thread_db.h [new file with mode: 0644]
gdb/lin-thread.c [new file with mode: 0644]

diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
new file mode 100644 (file)
index 0000000..f738050
--- /dev/null
@@ -0,0 +1,547 @@
+/* GNU/Linux on ARM native support.
+   Copyright 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.  */
+
+#include "defs.h"
+#include "inferior.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+
+#include <sys/user.h>
+#include <sys/ptrace.h>
+#include <sys/utsname.h>
+
+extern int arm_apcs_32;
+
+#define                typeNone                0x00
+#define                typeSingle              0x01
+#define                typeDouble              0x02
+#define                typeExtended            0x03
+#define        FPWORDS                 28
+#define                CPSR_REGNUM             16
+
+typedef union tagFPREG
+  {
+    unsigned int fSingle;
+    unsigned int fDouble[2];
+    unsigned int fExtended[3];
+  }
+FPREG;
+
+typedef struct tagFPA11
+  {
+    FPREG fpreg[8];            /* 8 floating point registers */
+    unsigned int fpsr;         /* floating point status register */
+    unsigned int fpcr;         /* floating point control register */
+    unsigned char fType[8];    /* type of floating point value held in
+                                  floating point registers.  */
+    int initflag;              /* NWFPE initialization flag.  */
+  }
+FPA11;
+
+/* The following variables are used to determine the version of the
+   underlying Linux operating system.  Examples:
+
+   Linux 2.0.35                 Linux 2.2.12
+   os_version = 0x00020023      os_version = 0x0002020c
+   os_major = 2                 os_major = 2
+   os_minor = 0                 os_minor = 2
+   os_release = 35              os_release = 12
+
+   Note: os_version = (os_major << 16) | (os_minor << 8) | os_release
+
+   These are initialized using get_linux_version() from
+   _initialize_arm_linux_nat().  */
+
+static unsigned int os_version, os_major, os_minor, os_release;
+
+static void
+fetch_nw_fpe_single (unsigned int fn, FPA11 * fpa11, unsigned int *pmem)
+{
+  unsigned int mem[3];
+
+  mem[0] = fpa11->fpreg[fn].fSingle;
+  mem[1] = 0;
+  mem[2] = 0;
+  supply_register (F0_REGNUM + fn, (char *) &mem[0]);
+}
+
+static void
+fetch_nw_fpe_double (unsigned int fn, FPA11 * fpa11, unsigned int *pmem)
+{
+  unsigned int mem[3];
+
+  mem[0] = fpa11->fpreg[fn].fDouble[1];
+  mem[1] = fpa11->fpreg[fn].fDouble[0];
+  mem[2] = 0;
+  supply_register (F0_REGNUM + fn, (char *) &mem[0]);
+}
+
+static void
+fetch_nw_fpe_none (unsigned int fn, FPA11 * fpa11, unsigned int *pmem)
+{
+  unsigned int mem[3] =
+  {0, 0, 0};
+
+  supply_register (F0_REGNUM + fn, (char *) &mem[0]);
+}
+
+static void
+fetch_nw_fpe_extended (unsigned int fn, FPA11 * fpa11, unsigned int *pmem)
+{
+  unsigned int mem[3];
+
+  mem[0] = fpa11->fpreg[fn].fExtended[0];      /* sign & exponent */
+  mem[1] = fpa11->fpreg[fn].fExtended[2];      /* ls bits */
+  mem[2] = fpa11->fpreg[fn].fExtended[1];      /* ms bits */
+  supply_register (F0_REGNUM + fn, (char *) &mem[0]);
+}
+
+static void
+store_nw_fpe_single (unsigned int fn, FPA11 * fpa11)
+{
+  unsigned int mem[3];
+
+  read_register_gen (F0_REGNUM + fn, (char *) &mem[0]);
+  fpa11->fpreg[fn].fSingle = mem[0];
+  fpa11->fType[fn] = typeSingle;
+}
+
+static void
+store_nw_fpe_double (unsigned int fn, FPA11 * fpa11)
+{
+  unsigned int mem[3];
+
+  read_register_gen (F0_REGNUM + fn, (char *) &mem[0]);
+  fpa11->fpreg[fn].fDouble[1] = mem[0];
+  fpa11->fpreg[fn].fDouble[0] = mem[1];
+  fpa11->fType[fn] = typeDouble;
+}
+
+void
+store_nw_fpe_extended (unsigned int fn, FPA11 * fpa11)
+{
+  unsigned int mem[3];
+
+  read_register_gen (F0_REGNUM + fn, (char *) &mem[0]);
+  fpa11->fpreg[fn].fExtended[0] = mem[0];      /* sign & exponent */
+  fpa11->fpreg[fn].fExtended[2] = mem[1];      /* ls bits */
+  fpa11->fpreg[fn].fExtended[1] = mem[2];      /* ms bits */
+  fpa11->fType[fn] = typeDouble;
+}
+
+/* Get the whole floating point state of the process and store the
+   floating point stack into registers[].  */
+
+static void
+fetch_fpregs (void)
+{
+  int ret, regno;
+  FPA11 fp;
+
+  /* Read the floating point state.  */
+  ret = ptrace (PT_GETFPREGS, inferior_pid, 0, &fp);
+  if (ret < 0)
+    {
+      warning ("Unable to fetch the floating point state.");
+      return;
+    }
+
+  /* Fetch fpsr.  */
+  supply_register (FPS_REGNUM, (char *) &fp.fpsr);
+
+  /* Fetch the floating point registers.  */
+  for (regno = F0_REGNUM; regno <= F7_REGNUM; regno++)
+    {
+      int fn = regno - F0_REGNUM;
+      unsigned int *p = (unsigned int *) &registers[REGISTER_BYTE (regno)];
+
+      switch (fp.fType[fn])
+       {
+       case typeSingle:
+         fetch_nw_fpe_single (fn, &fp, p);
+         break;
+
+       case typeDouble:
+         fetch_nw_fpe_double (fn, &fp, p);
+         break;
+
+       case typeExtended:
+         fetch_nw_fpe_extended (fn, &fp, p);
+         break;
+
+       default:
+         fetch_nw_fpe_none (fn, &fp, p);
+       }
+    }
+}
+
+/* Save the whole floating point state of the process using
+   the contents from registers[].  */
+
+static void
+store_fpregs (void)
+{
+  int ret, regno;
+  unsigned int mem[3];
+  FPA11 fp;
+
+  /* Store fpsr.  */
+  if (register_valid[FPS_REGNUM])
+    read_register_gen (FPS_REGNUM, (char *) &fp.fpsr);
+
+  /* Store the floating point registers.  */
+  for (regno = F0_REGNUM; regno <= F7_REGNUM; regno++)
+    {
+      if (register_valid[regno])
+       {
+         unsigned int fn = regno - F0_REGNUM;
+         switch (fp.fType[fn])
+           {
+           case typeSingle:
+             store_nw_fpe_single (fn, &fp);
+             break;
+
+           case typeDouble:
+             store_nw_fpe_double (fn, &fp);
+             break;
+
+           case typeExtended:
+             store_nw_fpe_extended (fn, &fp);
+             break;
+           }
+       }
+    }
+
+  ret = ptrace (PTRACE_SETFPREGS, inferior_pid, 0, &fp);
+  if (ret < 0)
+    {
+      warning ("Unable to store floating point state.");
+      return;
+    }
+}
+
+/* Fetch all general registers of the process and store into
+   registers[].  */
+
+static void
+fetch_regs (void)
+{
+  int ret, regno;
+  struct pt_regs regs;
+
+  ret = ptrace (PTRACE_GETREGS, inferior_pid, 0, &regs);
+  if (ret < 0)
+    {
+      warning ("Unable to fetch general registers.");
+      return;
+    }
+
+  for (regno = A1_REGNUM; regno < PC_REGNUM; regno++)
+    supply_register (regno, (char *) &regs.uregs[regno]);
+
+  if (arm_apcs_32)
+    supply_register (PS_REGNUM, (char *) &regs.uregs[CPSR_REGNUM]);
+  else
+    supply_register (PS_REGNUM, (char *) &regs.uregs[PC_REGNUM]);
+
+  regs.uregs[PC_REGNUM] = ADDR_BITS_REMOVE (regs.uregs[PC_REGNUM]);
+  supply_register (PC_REGNUM, (char *) &regs.uregs[PC_REGNUM]);
+}
+
+/* Store all general registers of the process from the values in
+   registers[].  */
+
+static void
+store_regs (void)
+{
+  int ret, regno;
+  struct pt_regs regs;
+
+  ret = ptrace (PTRACE_GETREGS, inferior_pid, 0, &regs);
+  if (ret < 0)
+    {
+      warning ("Unable to fetch general registers.");
+      return;
+    }
+
+  for (regno = A1_REGNUM; regno <= PC_REGNUM; regno++)
+    {
+      if (register_valid[regno])
+       read_register_gen (regno, (char *) &regs.uregs[regno]);
+    }
+
+  ret = ptrace (PTRACE_SETREGS, inferior_pid, 0, &regs);
+
+  if (ret < 0)
+    {
+      warning ("Unable to store general registers.");
+      return;
+    }
+}
+
+/* Fetch registers from the child process.  Fetch all registers if
+   regno == -1, otherwise fetch all general registers or all floating
+   point registers depending upon the value of regno.  */
+
+void
+fetch_inferior_registers (int regno)
+{
+  if ((regno < F0_REGNUM) || (regno > FPS_REGNUM))
+    fetch_regs ();
+
+  if (((regno >= F0_REGNUM) && (regno <= FPS_REGNUM)) || (regno == -1))
+    fetch_fpregs ();
+}
+
+/* Store registers back into the inferior.  Store all registers if
+   regno == -1, otherwise store all general registers or all floating
+   point registers depending upon the value of regno.  */
+
+void
+store_inferior_registers (int regno)
+{
+  if ((regno < F0_REGNUM) || (regno > FPS_REGNUM))
+    store_regs ();
+
+  if (((regno >= F0_REGNUM) && (regno <= FPS_REGNUM)) || (regno == -1))
+    store_fpregs ();
+}
+
+#ifdef GET_LONGJMP_TARGET
+
+/* Figure out where the longjmp will land.  We expect that we have
+   just entered longjmp and haven't yet altered r0, r1, so the
+   arguments are still in the registers.  (A1_REGNUM) points at 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. */
+
+#define LONGJMP_TARGET_SIZE    sizeof(int)
+#define JB_ELEMENT_SIZE                sizeof(int)
+#define JB_SL                  18
+#define JB_FP                  19
+#define JB_SP                  20
+#define JB_PC                  21
+
+int
+arm_get_longjmp_target (CORE_ADDR * pc)
+{
+  CORE_ADDR jb_addr;
+  char buf[LONGJMP_TARGET_SIZE];
+
+  jb_addr = read_register (A1_REGNUM);
+
+  if (target_read_memory (jb_addr + JB_PC * JB_ELEMENT_SIZE, buf,
+                         LONGJMP_TARGET_SIZE))
+    return 0;
+
+  *pc = extract_address (buf, LONGJMP_TARGET_SIZE);
+  return 1;
+}
+
+#endif /* GET_LONGJMP_TARGET */
+
+/*
+   Dynamic Linking on ARM Linux
+   ----------------------------
+
+   Note: PLT = procedure linkage table
+   GOT = global offset table
+
+   As much as possible, ELF dynamic linking defers the resolution of
+   jump/call addresses until the last minute. The technique used is
+   inspired by the i386 ELF design, and is based on the following
+   constraints.
+
+   1) The calling technique should not force a change in the assembly
+   code produced for apps; it MAY cause changes in the way assembly
+   code is produced for position independent code (i.e. shared
+   libraries).
+
+   2) The technique must be such that all executable areas must not be
+   modified; and any modified areas must not be executed.
+
+   To do this, there are three steps involved in a typical jump:
+
+   1) in the code
+   2) through the PLT
+   3) using a pointer from the GOT
+
+   When the executable or library is first loaded, each GOT entry is
+   initialized to point to the code which implements dynamic name
+   resolution and code finding.  This is normally a function in the
+   program interpreter (on ARM Linux this is usually ld-linux.so.2,
+   but it does not have to be).  On the first invocation, the function
+   is located and the GOT entry is replaced with the real function
+   address.  Subsequent calls go through steps 1, 2 and 3 and end up
+   calling the real code.
+
+   1) In the code: 
+
+   b    function_call
+   bl   function_call
+
+   This is typical ARM code using the 26 bit relative branch or branch
+   and link instructions.  The target of the instruction
+   (function_call is usually the address of the function to be called.
+   In position independent code, the target of the instruction is
+   actually an entry in the PLT when calling functions in a shared
+   library.  Note that this call is identical to a normal function
+   call, only the target differs.
+
+   2) In the PLT:
+
+   The PLT is a synthetic area, created by the linker. It exists in
+   both executables and libraries. It is an array of stubs, one per
+   imported function call. It looks like this:
+
+   PLT[0]:
+   str     lr, [sp, #-4]!       @push the return address (lr)
+   ldr     lr, [pc, #16]   @load from 6 words ahead
+   add     lr, pc, lr      @form an address for GOT[0]
+   ldr     pc, [lr, #8]!   @jump to the contents of that addr
+
+   The return address (lr) is pushed on the stack and used for
+   calculations.  The load on the second line loads the lr with
+   &GOT[3] - . - 20.  The addition on the third leaves:
+
+   lr = (&GOT[3] - . - 20) + (. + 8)
+   lr = (&GOT[3] - 12)
+   lr = &GOT[0]
+
+   On the fourth line, the pc and lr are both updated, so that:
+
+   pc = GOT[2]
+   lr = &GOT[0] + 8
+   = &GOT[2]
+
+   NOTE: PLT[0] borrows an offset .word from PLT[1]. This is a little
+   "tight", but allows us to keep all the PLT entries the same size.
+
+   PLT[n+1]:
+   ldr     ip, [pc, #4]    @load offset from gotoff
+   add     ip, pc, ip      @add the offset to the pc
+   ldr     pc, [ip]        @jump to that address
+   gotoff: .word   GOT[n+3] - .
+
+   The load on the first line, gets an offset from the fourth word of
+   the PLT entry.  The add on the second line makes ip = &GOT[n+3],
+   which contains either a pointer to PLT[0] (the fixup trampoline) or
+   a pointer to the actual code.
+
+   3) In the GOT:
+
+   The GOT contains helper pointers for both code (PLT) fixups and
+   data fixups.  The first 3 entries of the GOT are special. The next
+   M entries (where M is the number of entries in the PLT) belong to
+   the PLT fixups. The next D (all remaining) entries belong to
+   various data fixups. The actual size of the GOT is 3 + M + D.
+
+   The GOT is also a synthetic area, created by the linker. It exists
+   in both executables and libraries.  When the GOT is first
+   initialized , all the GOT entries relating to PLT fixups are
+   pointing to code back at PLT[0].
+
+   The special entries in the GOT are:
+
+   GOT[0] = linked list pointer used by the dynamic loader
+   GOT[1] = pointer to the reloc table for this module
+   GOT[2] = pointer to the fixup/resolver code
+
+   The first invocation of function call comes through and uses the
+   fixup/resolver code.  On the entry to the fixup/resolver code:
+
+   ip = &GOT[n+3]
+   lr = &GOT[2]
+   stack[0] = return address (lr) of the function call
+   [r0, r1, r2, r3] are still the arguments to the function call
+
+   This is enough information for the fixup/resolver code to work
+   with.  Before the fixup/resolver code returns, it actually calls
+   the requested function and repairs &GOT[n+3].  */
+
+CORE_ADDR
+arm_skip_solib_resolver (CORE_ADDR pc)
+{
+  /* FIXME */
+  return 0;
+}
+
+int
+arm_linux_register_u_addr (int blockend, int regnum)
+{
+  return blockend + REGISTER_BYTE (regnum);
+}
+
+int
+arm_linux_kernel_u_size (void)
+{
+  return (sizeof (struct user));
+}
+
+/* Extract from an array REGBUF containing the (raw) register state
+   a function return value of type TYPE, and copy that, in virtual format,
+   into VALBUF.  */
+
+void
+arm_linux_extract_return_value (struct type *type,
+                               char regbuf[REGISTER_BYTES],
+                               char *valbuf)
+{
+  /* ScottB: This needs to be looked at to handle the different
+     floating point emulators on ARM Linux.  Right now the code
+     assumes that fetch inferior registers does the right thing for
+     GDB.  I suspect this won't handle NWFPE registers correctly, nor
+     will the default ARM version (arm_extract_return_value()).  */
+
+  int regnum = (TYPE_CODE_FLT == TYPE_CODE (type)) ? F0_REGNUM : A1_REGNUM;
+  memcpy (valbuf, &regbuf[REGISTER_BYTE (regnum)], TYPE_LENGTH (type));
+}
+
+static unsigned int
+get_linux_version (unsigned int *vmajor,
+                  unsigned int *vminor,
+                  unsigned int *vrelease)
+{
+  struct utsname info;
+  char *pmajor, *pminor, *prelease, *tail;
+
+  if (-1 == uname (&info))
+    {
+      warning ("Unable to determine Linux version.");
+      return -1;
+    }
+
+  pmajor = strtok (info.release, ".");
+  pminor = strtok (NULL, ".");
+  prelease = strtok (NULL, ".");
+
+  *vmajor = (unsigned int) strtoul (pmajor, &tail, 0);
+  *vminor = (unsigned int) strtoul (pminor, &tail, 0);
+  *vrelease = (unsigned int) strtoul (prelease, &tail, 0);
+
+  return ((*vmajor << 16) | (*vminor << 8) | *vrelease);
+}
+
+void
+_initialize_arm_linux_nat (void)
+{
+  os_version = get_linux_version (&os_major, &os_minor, &os_release);
+}
diff --git a/gdb/config/arm/embed.mt b/gdb/config/arm/embed.mt
new file mode 100644 (file)
index 0000000..c854d17
--- /dev/null
@@ -0,0 +1,7 @@
+# Target: ARM embedded system
+TDEPFILES= arm-tdep.o remote-rdp.o remote-rdi.o
+TDEPLIBS= rdi-share/libangsd.a
+TM_FILE= tm-embed.h
+
+SIM_OBS = remote-sim.o
+SIM = ../sim/arm/libsim.a
diff --git a/gdb/config/arm/linux.mh b/gdb/config/arm/linux.mh
new file mode 100644 (file)
index 0000000..3c6dccc
--- /dev/null
@@ -0,0 +1,8 @@
+# Host: ARM based machine running GNU/Linux
+
+XM_FILE= xm-linux.h
+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 arm-linux-nat.o
diff --git a/gdb/config/arm/linux.mt b/gdb/config/arm/linux.mt
new file mode 100644 (file)
index 0000000..b85ae48
--- /dev/null
@@ -0,0 +1,5 @@
+# Target: ARM based machine running GNU/Linux
+TM_FILE= tm-linux.h
+TDEPFILES= arm-tdep.o
+
+GDBSERVER_DEPFILES= low-linux.o
diff --git a/gdb/config/arm/nm-linux.h b/gdb/config/arm/nm-linux.h
new file mode 100644 (file)
index 0000000..125d72f
--- /dev/null
@@ -0,0 +1,46 @@
+/* Definitions to make GDB run on an ARM based machine under GNU/Linux.
+   Copyright 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.  */
+
+#ifndef NM_ARMLINUX_H
+#define NM_ARMLINUX_H
+
+/* Return sizeof user struct to callers in less machine dependent routines */
+extern int kernel_u_size (void);
+#define KERNEL_U_SIZE  arm_linux_kernel_u_size()
+
+/* Override copies of {fetch,store}_inferior_registers in infptrace.c.  */
+#define FETCH_INFERIOR_REGISTERS
+
+/* Tell gdb that we can attach and detach other processes.  */
+#define ATTACH_DETACH
+
+extern int arm_register_u_addr (int, int);
+#define REGISTER_U_ADDR(addr, blockend, regno) \
+       { (addr) = arm_linux_register_u_addr((blockend), (regno)); }
+
+/* We define this if link.h is available, because with ELF we use SVR4 style
+   shared libraries. */
+
+#ifdef HAVE_LINK_H
+#define SVR4_SHARED_LIBS
+#include "solib.h"             /* Support for shared libraries. */
+#endif
+
+#endif /* NM_ARMLINUX_H */
diff --git a/gdb/config/arm/tm-embed.h b/gdb/config/arm/tm-embed.h
new file mode 100644 (file)
index 0000000..f42b4f2
--- /dev/null
@@ -0,0 +1,66 @@
+/* Definitions to target GDB to ARM embedded systems.
+   Copyright 1986-1989, 1991, 1993-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.  */
+
+#ifndef TM_ARMEMBED_H
+#define TM_ARMEMBED_H
+
+/* Include the common ARM definitions. */
+#include "arm/tm-arm.h"
+
+/* I don't know the real values for these.  */
+#define TARGET_UPAGES UPAGES
+#define TARGET_NBPG NBPG
+
+/* Address of end of stack space.  */
+#define STACK_END_ADDR (0x01000000 - (TARGET_UPAGES * TARGET_NBPG))
+
+/* The first 0x20 bytes are the trap vectors.  */
+#define LOWEST_PC      0x20
+
+/* Override defaults.  */
+
+#undef THUMB_LE_BREAKPOINT
+#define THUMB_LE_BREAKPOINT {0xbe,0xbe}       
+#undef THUMB_BE_BREAKPOINT
+#define THUMB_BE_BREAKPOINT {0xbe,0xbe}       
+
+/* Specify that for the native compiler variables for a particular
+   lexical context are listed after the beginning LBRAC instead of
+   before in the executables list of symbols.  */
+#define VARIABLES_INSIDE_BLOCK(desc, gcc_p) (!(gcc_p))
+
+/* Functions for dealing with Thumb call thunks.  */
+#define IN_SOLIB_CALL_TRAMPOLINE(pc, name)     arm_in_call_stub (pc, name)
+#define SKIP_TRAMPOLINE_CODE(pc)               arm_skip_stub (pc)
+extern int arm_in_call_stub PARAMS ((CORE_ADDR pc, char *name));
+extern CORE_ADDR arm_skip_stub PARAMS ((CORE_ADDR pc));
+
+/* Function to determine whether MEMADDR is in a Thumb function.  */
+extern int arm_pc_is_thumb PARAMS ((bfd_vma memaddr));
+
+/* Function to determine whether MEMADDR is in a call dummy called from
+   a Thumb function.  */
+extern int arm_pc_is_thumb_dummy PARAMS ((bfd_vma memaddr));
+
+
+#undef  IN_SIGTRAMP
+#define IN_SIGTRAMP(pc, name) 0
+
+#endif /* TM_ARMEMBED_H */
diff --git a/gdb/config/arm/tm-linux.h b/gdb/config/arm/tm-linux.h
new file mode 100644 (file)
index 0000000..27cc0d1
--- /dev/null
@@ -0,0 +1,122 @@
+/* Target definitions for GNU/Linux on ARM, for GDB.
+   Copyright 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.  */
+
+#ifndef TM_ARMLINUX_H
+#define TM_ARMLINUX_H
+
+/* Include the common ARM target definitions.  */
+#include "arm/tm-arm.h"
+
+#include "tm-linux.h"
+
+/* Target byte order on ARM Linux is not selectable.  */
+#undef TARGET_BYTE_ORDER_SELECTABLE_P
+#define TARGET_BYTE_ORDER_SELECTABLE_P         0
+
+/* Under ARM Linux the traditional way of performing a breakpoint is to
+   execute a particular software interrupt, rather than use a particular
+   undefined instruction to provoke a trap.  Upon exection of the software
+   interrupt the kernel stops the inferior with a SIGTRAP, and wakes the
+   debugger.  Since ARM Linux is little endian, and doesn't support Thumb
+   at the moment we redefined ARM_LE_BREAKPOINT to use the correct software
+   interrupt.  */
+#undef ARM_LE_BREAKPOINT
+#define ARM_LE_BREAKPOINT      {0x01,0x00,0x9f,0xef}
+
+/* This sequence of words used in the CALL_DUMMY are the following 
+   instructions:
+
+   mov  lr, pc
+   mov  pc, r4
+   swi bkpt_swi
+
+   Note this is 12 bytes.  */
+
+#undef CALL_DUMMY
+#define CALL_DUMMY {0xe1a0e00f, 0xe1a0f004, 0xef9f001}
+
+/* Extract from an array REGBUF containing the (raw) register state
+   a function return value of type TYPE, and copy that, in virtual format,
+   into VALBUF.  */
+extern void arm_linux_extract_return_value (struct type *, char[], char *);
+#undef EXTRACT_RETURN_VALUE
+#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \
+       arm_linux_extract_return_value ((TYPE), (REGBUF), (VALBUF))
+
+/* The first page is not writeable in ARM Linux.  */
+#define LOWEST_PC      0x8000
+
+/* Define NO_SINGLE_STEP if ptrace(PT_STEP,...) fails to function correctly
+   on ARM Linux.  This is the case on 2.0.x kernels, 2.1.x kernels and some 
+   2.2.x kernels.  This will include the implementation of single_step()
+   in armlinux-tdep.c.  See armlinux-ss.c for more details. */
+/* #define NO_SINGLE_STEP      1 */
+
+/* Offset to saved PC in sigcontext structure, from <asm/sigcontext.h> */
+#define SIGCONTEXT_PC_OFFSET   (sizeof(unsigned long) * 18)
+
+/* Figure out where the longjmp will land.  The code expects that longjmp
+   has just been entered and the code had not altered the registers, so
+   the arguments are are still in r0-r1.  r0 points at the jmp_buf structure
+   from which the target pc (JB_PC) is extracted.  This pc value is copied
+   into ADDR.  This routine returns true on success */
+extern int arm_get_longjmp_target (CORE_ADDR *);
+#define GET_LONGJMP_TARGET(addr)       arm_get_longjmp_target (addr)
+
+/* On ARM Linux, each call to a library routine goes through a small piece
+   of trampoline code in the ".plt" section.  The  wait_for_inferior() 
+   routine uses this macro to detect when we have stepped into one of 
+   these fragments.  We do not use lookup_solib_trampoline_symbol_by_pc,
+   because we cannot always find the shared library trampoline symbols.  */
+extern int in_plt_section (CORE_ADDR, char *);
+#define IN_SOLIB_CALL_TRAMPOLINE(pc, name) in_plt_section((pc), (name))
+
+/* On ARM Linux, a call to a library routine does not have to go through
+   any trampoline code.  */
+#define IN_SOLIB_RETURN_TRAMPOLINE(pc, name)   0
+
+/* If PC is in a shared library trampoline code, return the PC
+   where the function itself actually starts.  If not, return 0.  */
+extern CORE_ADDR find_solib_trampoline_target (CORE_ADDR pc);   
+#define SKIP_TRAMPOLINE_CODE(pc)  find_solib_trampoline_target (pc)
+
+/* When we call a function in a shared library, and the PLT sends us
+   into the dynamic linker to find the function's real address, we
+   need to skip over the dynamic linker call.  This function decides
+   when to skip, and where to skip to.  See the comments for
+   SKIP_SOLIB_RESOLVER at the top of infrun.c.  */
+extern CORE_ADDR arm_skip_solib_resolver (CORE_ADDR pc);
+#define SKIP_SOLIB_RESOLVER arm_skip_solib_resolver
+
+/* When we call a function in a shared library, and the PLT sends us
+   into the dynamic linker to find the function's real address, we
+   need to skip over the dynamic linker call.  This function decides
+   when to skip, and where to skip to.  See the comments for
+   SKIP_SOLIB_RESOLVER at the top of infrun.c.  */
+#if 0   
+#undef IN_SOLIB_DYNSYM_RESOLVE_CODE
+extern CORE_ADDR arm_in_solib_dynsym_resolve_code (CORE_ADDR pc, char *name);
+#define IN_SOLIB_DYNSYM_RESOLVE_CODE  arm_in_solib_dynsym_resolve_code
+/* ScottB: Current definition is 
+extern CORE_ADDR in_svr4_dynsym_resolve_code (CORE_ADDR pc, char *name);
+#define IN_SOLIB_DYNSYM_RESOLVE_CODE  in_svr4_dynsym_resolve_code */
+#endif
+
+#endif /* TM_ARMLINUX_H */
diff --git a/gdb/config/arm/xm-linux.h b/gdb/config/arm/xm-linux.h
new file mode 100644 (file)
index 0000000..ef724b5
--- /dev/null
@@ -0,0 +1,37 @@
+/* Host definitions for ARM GNU/Linux, for GDB, the GNU debugger.
+   Copyright 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.  */
+
+#ifndef XM_ARMLINUX_H
+#define XM_ARMLINUX_H
+
+#define HOST_BYTE_ORDER                LITTLE_ENDIAN
+
+#define HAVE_TERMIOS
+
+/* This is the amount to subtract from u.u_ar0
+   to get the offset in the core file of the register values.  */
+#define KERNEL_U_ADDR 0x0
+
+#define NEED_POSIX_SETPGID
+
+/* Need R_OK etc, but USG isn't defined.  */
+#include <unistd.h>
+
+#endif /* XM_ARMLINUX_H */
diff --git a/gdb/gdb_proc_service.h b/gdb/gdb_proc_service.h
new file mode 100644 (file)
index 0000000..dfbf964
--- /dev/null
@@ -0,0 +1,24 @@
+typedef enum {
+  PS_OK,          /* generic "call succeeded" */
+  PS_ERR,         /* generic. */
+  PS_BADPID,      /* bad process handle */
+  PS_BADLID,      /* bad lwp identifier */
+  PS_BADADDR,     /* bad address */
+  PS_NOSYM,       /* p_lookup() could not find given symbol */
+        PS_NOFREGS
+  /*
+   * FPU register set not available for given
+   * lwp
+   */
+}       ps_err_e;
+
+typedef unsigned int  lwpid_t;
+typedef unsigned long paddr_t;
+typedef unsigned long psaddr_t;
+
+
+typedef gregset_t  prgregset_t;                /* BOGUS BOGUS BOGUS */
+typedef fpregset_t prfpregset_t;       /* BOGUS BOGUS BOGUS */
+
+
+struct ps_prochandle;          /* user defined. */
diff --git a/gdb/gdb_thread_db.h b/gdb/gdb_thread_db.h
new file mode 100644 (file)
index 0000000..5bb1963
--- /dev/null
@@ -0,0 +1,427 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _THREAD_DB_H
+#define _THREAD_DB_H   1
+
+/* This is the debugger interface for the LinuxThreads library.  It is
+   modelled closely after the interface with same names in Solaris with
+   the goal to share the same code in the debugger.  */
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+/*#include <sys/ucontext.h>*/
+
+
+/* Error codes of the library.  */
+typedef enum
+{
+  TD_OK,       /* No error.  */
+  TD_ERR,      /* No further specified error.  */
+  TD_NOTHR,    /* No matching thread found.  */
+  TD_NOSV,     /* No matching synchronization handle found.  */
+  TD_NOLWP,    /* No matching light-weighted process found.  */
+  TD_BADPH,    /* Invalid process handle.  */
+  TD_BADTH,    /* Invalid thread handle.  */
+  TD_BADSH,    /* Invalid synchronization handle.  */
+  TD_BADTA,    /* Invalid thread agent.  */
+  TD_BADKEY,   /* Invalid key.  */
+  TD_NOMSG,    /* No event available.  */
+  TD_NOFPREGS, /* No floating-point register content available.  */
+  TD_NOLIBTHREAD,      /* Application not linked with thread library.  */
+  TD_NOEVENT,  /* Requested event is not supported.  */
+  TD_NOCAPAB,  /* Capability not available.  */
+  TD_DBERR,    /* Internal debug library error.  */
+  TD_NOAPLIC,  /* Operation is not applicable.  */
+  TD_NOTSD,    /* No thread-specific data available.  */
+  TD_MALLOC,   /* Out of memory.  */
+  TD_PARTIALREG,/* Not entire register set was read or written.  */
+  TD_NOXREGS   /* X register set not available for given thread.  */
+} td_err_e;
+
+
+/* Possible thread states.  TD_THR_ANY_STATE is a pseudo-state used to
+   select threads regardless of state in td_ta_thr_iter().  */
+typedef enum
+{
+  TD_THR_ANY_STATE,
+  TD_THR_UNKNOWN,
+  TD_THR_STOPPED,
+  TD_THR_RUN,
+  TD_THR_ACTIVE,
+  TD_THR_ZOMBIE,
+  TD_THR_SLEEP,
+  TD_THR_STOPPED_ASLEEP
+} td_thr_state_e;
+
+/* Thread type: user or system.  TD_THR_ANY_TYPE is a pseudo-type used
+   to select threads regardless of type in td_ta_thr_iter().  */
+typedef enum
+{
+  TD_THR_ANY_TYPE,
+  TD_THR_USER,
+  TD_THR_SYSTEM
+} td_thr_type_e;
+
+
+/* Types of the debugging library.  */
+
+/* Addresses.  */
+/*typedef void *psaddr_t;*/
+
+/* Handle for a process.  This type is opaque.  */
+typedef struct td_thragent td_thragent_t;
+
+/* The actual thread handle type.  This is also opaque.  */
+typedef struct td_thrhandle
+{
+  td_thragent_t *th_ta_p;
+  psaddr_t th_unique;
+} td_thrhandle_t;
+
+
+/* Flags for `td_ta_thr_iter'.  */
+#define TD_THR_ANY_USER_FLAGS  0xffffffff
+#define TD_THR_LOWEST_PRIORITY -20
+#define TD_SIGNO_MASK          NULL
+
+
+#define TD_EVENTSIZE   2
+#define BT_UISHIFT     5 /* log base 2 of BT_NBIPUI, to extract word index */
+#define BT_NBIPUI      (1 << BT_UISHIFT)       /* n bits per uint */
+#define BT_UIMASK      (BT_NBIPUI - 1)         /* to extract bit index */
+
+/* Bitmask of enabled events. */
+typedef struct td_thr_events
+{
+  uint32_t event_bits[TD_EVENTSIZE];
+} td_thr_events_t;
+
+/* Event set manipulation macros. */
+#define __td_eventmask(n) \
+  (UINT32_C (1) << (((n) - 1) & BT_UIMASK))
+#define __td_eventword(n) \
+  ((UINT32_C ((n) - 1)) >> BT_UISHIFT)
+
+#define td_event_emptyset(setp) \
+  do {                                                                       \
+    int __i;                                                                 \
+    for (__i = TD_EVENTSIZE; __i > 0; --__i)                                 \
+      (setp)->event_bits[__i - 1] = 0;                                       \
+  } while (0)
+
+#define td_event_fillset(setp) \
+  do {                                                                       \
+    int __i;                                                                 \
+    for (__i = TD_EVENTSIZE; __i > 0; --__i)                                 \
+      (setp)->event_bits[__i - 1] = UINT32_C (0xffffffff);                   \
+  } while (0)
+
+#define td_event_addset(setp, n) \
+  (((setp)->event_bits[__td_eventword (n)]) |= __td_eventmask (n))
+#define td_event_delset(setp, n) \
+  (((setp)->event_bits[__td_eventword (n)]) &= ~__td_eventmask (n))
+#define td_eventismember(setp, n) \
+  (__td_eventmask (n) & ((setp)->event_bits[__td_eventword (n)]))
+#if TD_EVENTSIZE == 2
+# define td_eventisempty(setp) \
+  (!((setp)->event_bits[0]) && !((setp)->event_bits[1]))
+#else
+# error "td_eventisempty must be changed to match TD_EVENTSIZE"
+#endif
+
+/* Events reportable by the thread implementation.  */
+typedef enum
+{
+  TD_ALL_EVENTS,                /* Pseudo-event number.  */
+  TD_EVENT_NONE = TD_ALL_EVENTS, /* Depends on context.  */
+  TD_READY,                     /* Is executable now. */
+  TD_SLEEP,                     /* Blocked in a synchronization obj.  */
+  TD_SWITCHTO,                  /* Now assigned to a process.  */
+  TD_SWITCHFROM,                /* Not anymore assigned to a process.  */
+  TD_LOCK_TRY,                  /* Trying to get an unavailable lock.  */
+  TD_CATCHSIG,                  /* Signal posted to the thread.  */
+  TD_IDLE,                      /* Process getting idle.  */
+  TD_CREATE,                    /* New thread created.  */
+  TD_DEATH,                     /* Thread terminated.  */
+  TD_PREEMPT,                   /* Preempted.  */
+  TD_PRI_INHERIT,               /* Inherited elevated priority.  */
+  TD_REAP,                      /* Reaped.  */
+  TD_CONCURRENCY,               /* Number of processes changing.  */
+  TD_TIMEOUT,                   /* Conditional variable wait timed out.  */
+  TD_MIN_EVENT_NUM = TD_READY,
+  TD_MAX_EVENT_NUM = TD_TIMEOUT,
+  TD_EVENTS_ENABLE = 31                /* Event reporting enabled.  */
+} td_event_e;
+
+/* Values representing the different ways events are reported.  */
+typedef enum
+{
+  NOTIFY_BPT,                  /* User must insert breakpoint at u.bptaddr. */
+  NOTIFY_AUTOBPT,              /* Breakpoint at u.bptaddr is automatically
+                                  inserted.  */
+  NOTIFY_SYSCALL               /* System call u.syscallno will be invoked.  */
+} td_notify_e;
+
+/* Description how event type is reported.  */
+typedef struct td_notify
+{
+  td_notify_e type;            /* Way the event is reported.  */
+  union
+  {
+    psaddr_t bptaddr;          /* Address of breakpoint.  */
+    int syscallno;             /* Number of system call used.  */
+  } u;
+} td_notify_t;
+
+/* Structure used to report event.  */
+typedef struct td_event_msg
+{
+  td_event_e event;            /* Event type being reported.  */
+  const td_thrhandle_t *th_p;  /* Thread reporting the event.  */
+  union
+  {
+# if 0
+    td_synchandle_t *sh;       /* Handle of synchronization object.  */
+#endif
+    uintptr_t data;            /* Event specific data.  */
+  } msg;
+} td_event_msg_t;
+
+
+/* Gathered statistics about the process.  */
+typedef struct td_ta_stats
+{
+  int nthreads;                /* Total number of threads in use.  */
+  int r_concurrency;           /* Concurrency level requested by user.  */
+  int nrunnable_num;           /* Average runnable threads, numerator.  */
+  int nrunnable_den;           /* Average runnable threads, denominator.  */
+  int a_concurrency_num;       /* Achieved concurrency level, numerator.  */
+  int a_concurrency_den;       /* Achieved concurrency level, denominator.  */
+  int nlwps_num;               /* Average number of processes in use,
+                                  numerator.  */
+  int nlwps_den;               /* Average number of processes in use,
+                                  denominator.  */
+  int nidle_num;               /* Average number of idling processes,
+                                  numerator.  */
+  int nidle_den;               /* Average number of idling processes,
+                                  denominator.  */
+} td_ta_stats_t;
+
+
+/* Since Sun's library is based on Solaris threads we have to define a few
+   types to map them to POSIX threads.  */
+typedef pthread_t thread_t;
+typedef pthread_key_t thread_key_t;
+
+/* Linux has different names for the register set types.  */
+/*typedef gregset_t prgregset_t;*/
+/*typedef fpregset_t prfpregset_t;*/
+
+
+/* Callback for iteration over threads.  */
+typedef int td_thr_iter_f __P ((const td_thrhandle_t *, void *));
+
+/* Callback for iteration over thread local data.  */
+typedef int td_key_iter_f __P ((thread_key_t, void (*) (void *), void *));
+
+
+
+/* Forward declaration.  This has to be defined by the user.  */
+struct ps_prochandle;
+
+/* We don't have any differences between processes and threads, therefore
+   have only one PID type.  */
+/*typedef pid_t lwpid_t;*/
+
+
+/* Information about the thread.  */
+typedef struct td_thrinfo
+{
+  td_thragent_t *ti_ta_p;              /* Process handle.  */
+  unsigned int ti_user_flags;          /* Unused.  */
+  thread_t ti_tid;                     /* Thread ID returned by
+                                          pthread_create().  */
+  char *ti_tls;                                /* Pointer to thread-local data.  */
+  psaddr_t ti_startfunc;               /* Start function passed to
+                                          pthread_create().  */
+  psaddr_t ti_stkbase;                 /* Base of thread's stack.  */
+  long int ti_stksize;                 /* Size of thread's stack.  */
+  psaddr_t ti_ro_area;                 /* Unused.  */
+  int ti_ro_size;                      /* Unused.  */
+  td_thr_state_e ti_state;             /* Thread state.  */
+  unsigned char ti_db_suspended;       /* Nonzero if suspended by debugger. */
+  td_thr_type_e ti_type;               /* Type of the thread (system vs
+                                          user thread).  */
+  intptr_t ti_pc;                      /* Unused.  */
+  intptr_t ti_sp;                      /* Unused.  */
+  short int ti_flags;                  /* Unused.  */
+  int ti_pri;                          /* Thread priority.  */
+  lwpid_t ti_lid;                      /* Unused.  */
+  sigset_t ti_sigmask;                 /* Signal mask.  */
+  unsigned char ti_traceme;            /* Nonzero if event reporting
+                                          enabled.  */
+  unsigned char ti_preemptflag;                /* Unused.  */
+  unsigned char ti_pirecflag;          /* Unused.  */
+  sigset_t ti_pending;                 /* Set of pending signals.  */
+  td_thr_events_t ti_events;           /* Set of enabled events.  */
+} td_thrinfo_t;
+
+
+
+/* Prototypes for exported library functions.  */
+
+/* Initialize the thread debug support library.  */
+extern td_err_e td_init (void);
+
+/* Historical relict.  Should not be used anymore.  */
+extern td_err_e td_log (void);
+
+/* Generate new thread debug library handle for process PS.  */
+extern td_err_e td_ta_new (struct ps_prochandle *__ps, td_thragent_t **__ta);
+
+/* Free resources allocated for TA.  */
+extern td_err_e td_ta_delete (td_thragent_t *__ta);
+
+/* Get number of currently running threads in process associated with TA.  */
+extern td_err_e td_ta_get_nthreads (const td_thragent_t *__ta, int *__np);
+
+/* Return process handle passed in `td_ta_new' for process associated with
+   TA.  */
+extern td_err_e td_ta_get_ph (const td_thragent_t *__ta,
+                             struct ps_prochandle **__ph);
+
+/* Map thread library handle PT to thread debug library handle for process
+   associated with TA and store result in *TH.  */
+extern td_err_e td_ta_map_id2thr (const td_thragent_t *__ta, pthread_t __pt,
+                                 td_thrhandle_t *__th);
+
+/* Map process ID LWPID to thread debug library handle for process
+   associated with TA and store result in *TH.  */
+extern td_err_e td_ta_map_lwp2thr (const td_thragent_t *__ta, lwpid_t __lwpid,
+                                  td_thrhandle_t *__th);
+
+
+/* Call for each thread in a process associated with TA the callback function
+   CALLBACK.  */
+extern td_err_e td_ta_thr_iter (const td_thragent_t *__ta,
+                               td_thr_iter_f *__callback, void *__cbdata_p,
+                               td_thr_state_e __state, int __ti_pri,
+                               sigset_t *__ti_sigmask_p,
+                               unsigned int __ti_user_flags);
+
+/* Call for each defined thread local data entry the callback function KI.  */
+extern td_err_e td_ta_tsd_iter (const td_thragent_t *__ta, td_key_iter_f *__ki,
+                               void *__p);
+
+
+/* Get event address for EVENT.  */
+extern td_err_e td_ta_event_addr (const td_thragent_t *__ta,
+                                 td_event_e __event, td_notify_t *__ptr);
+
+
+/* Set suggested concurrency level for process associated with TA.  */
+extern td_err_e td_ta_setconcurrency (const td_thragent_t *__ta, int __level);
+
+
+/* Enable collecting statistics for process associated with TA.  */
+extern td_err_e td_ta_enable_stats (const td_thragent_t *__ta, int __enable);
+
+/* Reset statistics.  */
+extern td_err_e td_ta_reset_stats (const td_thragent_t *__ta);
+
+/* Retrieve statistics from process associated with TA.  */
+extern td_err_e td_ta_get_stats (const td_thragent_t *__ta,
+                                td_ta_stats_t *__statsp);
+
+
+/* Validate that TH is a thread handle.  */
+extern td_err_e td_thr_validate (const td_thrhandle_t *__th);
+
+/* Return information about thread TH.  */
+extern td_err_e td_thr_get_info (const td_thrhandle_t *__th,
+                                td_thrinfo_t *__infop);
+
+/* Retrieve floating-point register contents of process running thread TH.  */
+extern td_err_e td_thr_getfpregs (const td_thrhandle_t *__th,
+                                 prfpregset_t *__regset);
+
+/* Retrieve general register contents of process running thread TH.  */
+extern td_err_e td_thr_getgregs (const td_thrhandle_t *__th,
+                                prgregset_t __gregs);
+
+/* Retrieve extended register contents of process running thread TH.  */
+extern td_err_e td_thr_getxregs (const td_thrhandle_t *__th, void *__xregs);
+
+/* Get size of extended register set of process running thread TH.  */
+extern td_err_e td_thr_getxregsize (const td_thrhandle_t *__th, int *__sizep);
+
+/* Set floating-point register contents of process running thread TH.  */
+extern td_err_e td_thr_setfpregs (const td_thrhandle_t *__th,
+                                 const prfpregset_t *__fpregs);
+
+/* Set general register contents of process running thread TH.  */
+extern td_err_e td_thr_setgregs (const td_thrhandle_t *__th,
+                                prgregset_t __gregs);
+
+/* Set extended register contents of process running thread TH.  */
+extern td_err_e td_thr_setxregs (const td_thrhandle_t *__th,
+                                const void *__addr);
+
+
+/* Enable reporting for EVENT for thread TH.  */
+extern td_err_e td_thr_event_enable (const td_thrhandle_t *__th, int __event);
+
+/* Enable EVENT for thread TH.  */
+extern td_err_e td_thr_set_event (const td_thrhandle_t *__th,
+                                 td_thr_events_t *__event);
+
+/* Disable EVENT for thread TH.  */
+extern td_err_e td_thr_clear_event (const td_thrhandle_t *__th,
+                                   td_thr_events_t *__event);
+
+/* Get event message for thread TH.  */
+extern td_err_e td_thr_event_getmsg (const td_thrhandle_t *__th,
+                                    td_event_msg_t *__msg);
+
+
+/* Set priority of thread TH.  */
+extern td_err_e td_thr_setprio (const td_thrhandle_t *__th, int __prio);
+
+
+/* Set pending signals for thread TH.  */
+extern td_err_e td_thr_setsigpending (const td_thrhandle_t *__th,
+                                     unsigned char __n, const sigset_t *__ss);
+
+/* Set signal mask for thread TH.  */
+extern td_err_e td_thr_sigsetmask (const td_thrhandle_t *__th,
+                                  const sigset_t *__ss);
+
+
+/* Return thread local data associated with key TK in thread TH.  */
+extern td_err_e td_thr_tsd (const td_thrhandle_t *__th,
+                           const thread_key_t __tk, void **__data);
+
+
+/* Suspend execution of thread TH.  */
+extern td_err_e td_thr_dbsuspend (const td_thrhandle_t *__th);
+
+/* Resume execution of thread TH.  */
+extern td_err_e td_thr_dbresume (const td_thrhandle_t *__th);
+
+#endif /* thread_db.h */
diff --git a/gdb/lin-thread.c b/gdb/lin-thread.c
new file mode 100644 (file)
index 0000000..2f255c0
--- /dev/null
@@ -0,0 +1,2140 @@
+/* Multi-threaded debugging support for the thread_db interface,
+   used on operating systems such as Solaris and Linux.
+   Copyright 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 a thread_stratum target that sits on top of
+   a normal process_stratum target (such as procfs or ptrace).  The
+   process_stratum target must install this thread_stratum target when
+   it detects the presence of the thread_db shared library.
+
+   This module will then use the thread_db API to add thread-awareness
+   to the functionality provided by the process_stratum target (or in
+   some cases, to add user-level thread awareness on top of the 
+   kernel-level thread awareness that is already provided by the 
+   process_stratum target).
+
+   Solaris threads (for instance) are a multi-level thread implementation;
+   the kernel provides a Light Weight Process (LWP) which the procfs 
+   process_stratum module is aware of.  This module must then mediate
+   the relationship between kernel LWP threads and user (eg. posix)
+   threads.
+
+   Linux threads are likely to be different -- but the thread_db 
+   library API should make the difference largely transparent to GDB.
+
+   */
+
+/* The thread_db API provides a number of functions that give the caller
+   access to the inner workings of the child process's thread library. 
+   We will be using the following (others may be added):
+
+   td_thr_validate             Confirm valid "live" thread
+   td_thr_get_info             Get info about a thread
+   td_thr_getgregs             Get thread's general registers
+   td_thr_getfpregs            Get thread's floating point registers
+   td_thr_setgregs             Set thread's general registers
+   td_thr_setfpregs            Set thread's floating point registers
+   td_ta_map_id2thr            Get thread handle from thread id
+   td_ta_map_lwp2thr           Get thread handle from LWP id
+   td_ta_thr_iter              Iterate over all threads (with callback)
+
+   In return, the debugger has to provide certain services to the 
+   thread_db library.  Some of these aren't actually required to do
+   anything in practice.  For instance, the thread_db expects to be
+   able to stop the child process and start it again: but in our
+   context, the child process will always be stopped already when we
+   invoke the thread_db library, so the functions that we provide for
+   the library to stop and start the child process are no-ops.
+
+   Here is the list of functions which we export to the thread_db
+   library, divided into no-op functions vs. functions that actually
+   have to do something:
+
+   No-op functions:
+
+   ps_pstop                    Stop the child process
+   ps_pcontinue                        Continue the child process
+   ps_lstop                    Stop a specific LWP (kernel thread)
+   ps_lcontinue                        Continue an LWP
+   ps_lgetxregsize             Get size of LWP's xregs (sparc)
+   ps_lgetxregs                        Get LWP's xregs (sparc)
+   ps_lsetxregs                        Set LWP's xregs (sparc)
+
+   Functions that have to do useful work:
+
+   ps_pglobal_lookup           Get the address of a global symbol
+   ps_pdread                   Read memory, data segment
+   ps_ptread                   Read memory, text segment
+   ps_pdwrite                  Write memory, data segment
+   ps_ptwrite                  Write memory, text segment
+   ps_lgetregs                 Get LWP's general registers
+   ps_lgetfpregs               Get LWP's floating point registers
+   ps_lsetregs                 Set LWP's general registers
+   ps_lsetfpregs               Set LWP's floating point registers
+   ps_lgetLDT                  Get LWP's Local Descriptor Table (x86)
+   
+   Thus, if we ask the thread_db library to give us the general registers
+   for user thread X, thread_db may figure out that user thread X is 
+   actually mapped onto kernel thread Y.  Thread_db does not know how
+   to obtain the registers for kernel thread Y, but GDB does, so thread_db
+   turns the request right back to us via the ps_lgetregs callback.  */
+
+#include "defs.h"
+#include "gdbthread.h"
+#include "target.h"
+#include "inferior.h"
+#include "gdbcmd.h"
+
+#ifdef HAVE_WAIT_H
+#include <wait.h>
+#else
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#endif
+
+/* "wait.h" fills in the gaps left by <wait.h> */
+#include "wait.h"
+
+#include <time.h>
+
+#if defined(USE_PROC_FS) || defined(HAVE_GREGSET_T)
+#include <sys/procfs.h>
+#endif
+
+#if defined (HAVE_PROC_SERVICE_H)
+#include <proc_service.h>      /* defines incoming API (ps_* callbacks) */
+#else
+#include "gdb_proc_service.h"
+#endif
+
+#if defined HAVE_STDINT_H      /* Pre-5.2 systems don't have this header */
+#if defined (HAVE_THREAD_DB_H)
+#include <thread_db.h>         /* defines outgoing API (td_thr_* calls) */
+#else
+#include "gdb_thread_db.h"
+#endif
+
+#include <dlfcn.h>             /* dynamic library interface */
+
+#ifndef TIDGET
+#define TIDGET(PID)            (((PID) & 0x7fffffff) >> 16)
+#define PIDGET(PID)            (((PID) & 0xffff))
+#define MERGEPID(PID, TID)     (((PID) & 0xffff) | ((TID) << 16))
+#endif
+
+/* Macros for superimposing PID and TID into inferior_pid.  */
+#define THREAD_FLAG            0x80000000
+#define is_thread(ARG)         (((ARG) & THREAD_FLAG) != 0)
+#define is_lwp(ARG)            (((ARG) & THREAD_FLAG) == 0)
+#define GET_LWP(PID)           TIDGET (PID)
+#define GET_THREAD(PID)                TIDGET (PID)
+#define BUILD_LWP(TID, PID)    MERGEPID (PID, TID)
+#define BUILD_THREAD(TID, PID) (MERGEPID (PID, TID) | THREAD_FLAG)
+
+/*
+ * target_beneath is a pointer to the target_ops underlying this one.
+ */
+
+static struct target_ops *target_beneath;
+
+
+/*
+ * target vector defined in this module:
+ */
+
+static struct target_ops thread_db_ops;
+
+/*
+ * Typedefs required to resolve differences between the thread_db
+ * and proc_service API defined on different versions of Solaris:
+ */
+
+#if defined(PROC_SERVICE_IS_OLD)
+typedef const struct ps_prochandle *gdb_ps_prochandle_t;
+typedef char *gdb_ps_read_buf_t;
+typedef char *gdb_ps_write_buf_t;
+typedef int gdb_ps_size_t;
+#else
+typedef struct ps_prochandle *gdb_ps_prochandle_t;
+typedef void *gdb_ps_read_buf_t;
+typedef const void *gdb_ps_write_buf_t;
+typedef size_t gdb_ps_size_t;
+#endif
+
+/* 
+ * proc_service callback functions, called by thread_db.
+ */
+
+ps_err_e
+ps_pstop (gdb_ps_prochandle_t ph)              /* Process stop */
+{
+  return PS_OK;
+}
+
+ps_err_e
+ps_pcontinue (gdb_ps_prochandle_t ph)          /* Process continue */
+{
+  return PS_OK;
+}
+
+ps_err_e
+ps_lstop (gdb_ps_prochandle_t ph,              /* LWP stop */
+         lwpid_t lwpid)
+{
+  return PS_OK;
+}
+
+ps_err_e
+ps_lcontinue (gdb_ps_prochandle_t ph,          /* LWP continue */
+             lwpid_t lwpid)
+{
+  return PS_OK;
+}
+
+ps_err_e
+ps_lgetxregsize (gdb_ps_prochandle_t ph,       /* Get XREG size */
+                lwpid_t lwpid,
+                int *xregsize)
+{
+  return PS_OK;
+}
+
+ps_err_e
+ps_lgetxregs (gdb_ps_prochandle_t ph,          /* Get XREGS */
+             lwpid_t lwpid,
+             caddr_t xregset)
+{
+  return PS_OK;
+}
+
+ps_err_e
+ps_lsetxregs (gdb_ps_prochandle_t ph,          /* Set XREGS */
+             lwpid_t lwpid,
+             caddr_t xregset)
+{
+  return PS_OK;
+}
+
+void
+ps_plog (const char *fmt, ...)
+{
+  va_list args;
+
+  va_start (args, fmt);
+  vfprintf_filtered (gdb_stderr, fmt, args);
+}
+
+/* Look up a symbol in GDB's global symbol table.
+   Return the symbol's address.
+   FIXME: it would be more correct to look up the symbol in the context 
+   of the LD_OBJECT_NAME provided.  However we're probably fairly safe 
+   as long as there aren't name conflicts with other libraries.  */
+
+ps_err_e
+ps_pglobal_lookup (gdb_ps_prochandle_t ph,
+                  const char *ld_object_name,  /* the library name */
+                  const char *ld_symbol_name,  /* the symbol name */
+                  paddr_t    *ld_symbol_addr)  /* return the symbol addr */
+{
+  struct minimal_symbol *ms;
+
+  ms = lookup_minimal_symbol (ld_symbol_name, NULL, NULL);
+
+  if (!ms)
+    return PS_NOSYM;
+
+  *ld_symbol_addr = SYMBOL_VALUE_ADDRESS (ms);
+
+  return PS_OK;
+}
+
+/* Worker function for all memory reads and writes: */
+static ps_err_e rw_common (const struct ps_prochandle *ph, 
+                          paddr_t addr,
+                          char *buf, 
+                          int size, 
+                          int write_p);
+
+/* target_xfer_memory direction consts */
+enum {PS_READ = 0, PS_WRITE = 1};
+
+ps_err_e
+ps_pdread (gdb_ps_prochandle_t ph,     /* read from data segment */
+          paddr_t             addr,
+          gdb_ps_read_buf_t   buf,
+          gdb_ps_size_t       size)
+{
+  return rw_common (ph, addr, buf, size, PS_READ);
+}
+
+ps_err_e
+ps_pdwrite (gdb_ps_prochandle_t ph,    /* write to data segment */
+           paddr_t             addr,
+           gdb_ps_write_buf_t  buf,
+           gdb_ps_size_t       size)
+{
+  return rw_common (ph, addr, (char *) buf, size, PS_WRITE);
+}
+
+ps_err_e
+ps_ptread (gdb_ps_prochandle_t ph,     /* read from text segment */
+          paddr_t             addr,
+          gdb_ps_read_buf_t   buf,
+          gdb_ps_size_t       size)
+{
+  return rw_common (ph, addr, buf, size, PS_READ);
+}
+
+ps_err_e
+ps_ptwrite (gdb_ps_prochandle_t ph,    /* write to text segment */
+           paddr_t             addr,
+           gdb_ps_write_buf_t  buf,
+           gdb_ps_size_t       size)
+{
+  return rw_common (ph, addr, (char *) buf, size, PS_WRITE);
+}
+
+static struct cleanup *save_inferior_pid    (void);
+static void            restore_inferior_pid (void *saved_pid);
+static char *thr_err_string   (td_err_e);
+static char *thr_state_string (td_thr_state_e);
+
+struct ps_prochandle {
+  int pid;
+};
+
+struct ps_prochandle main_prochandle;
+td_thragent_t *      main_threadagent;
+
+/* 
+ * Common proc_service routine for reading and writing memory.  
+ */
+
+/* FIXME: once we've munged the inferior_pid, why can't we
+   simply call target_read/write_memory and return?  */
+
+
+static ps_err_e
+rw_common (const struct ps_prochandle *ph,
+          paddr_t addr,
+          char   *buf,
+          int     size,
+          int     write_p)
+{
+  struct cleanup *old_chain = save_inferior_pid ();
+  int to_do = size;
+  int done  = 0;
+
+  inferior_pid = main_prochandle.pid;
+
+  while (to_do > 0)
+    {
+      done = current_target.to_xfer_memory (addr, buf, size, write_p, 
+                                           &current_target);
+      if (done <= 0)
+       {
+         if (write_p == PS_READ)
+           print_sys_errmsg ("rw_common (): read", errno);
+         else
+           print_sys_errmsg ("rw_common (): write", errno);
+
+         return PS_ERR;
+       }
+      to_do -= done;
+      buf   += done;
+    }
+  do_cleanups (old_chain);
+  return PS_OK;
+}
+
+/* Cleanup functions used by the register callbacks
+   (which have to manipulate the global inferior_pid).  */
+
+ps_err_e
+ps_lgetregs (gdb_ps_prochandle_t ph,           /* Get LWP general regs */
+            lwpid_t     lwpid,
+            prgregset_t gregset)
+{
+  struct cleanup *old_chain = save_inferior_pid ();
+
+  inferior_pid = BUILD_LWP (lwpid, main_prochandle.pid);
+  current_target.to_fetch_registers (-1);
+
+  fill_gregset (gregset, -1);
+  do_cleanups (old_chain);
+
+  return PS_OK;
+}
+
+ps_err_e
+ps_lsetregs (gdb_ps_prochandle_t ph,           /* Set LWP general regs */
+            lwpid_t           lwpid,
+            const prgregset_t gregset)
+{
+  struct cleanup *old_chain = save_inferior_pid ();
+
+  inferior_pid = BUILD_LWP (lwpid, main_prochandle.pid);
+  supply_gregset (gregset);
+  current_target.to_store_registers (-1);
+  do_cleanups (old_chain);
+  return PS_OK;
+}
+
+ps_err_e
+ps_lgetfpregs (gdb_ps_prochandle_t ph,         /* Get LWP float regs */
+              lwpid_t       lwpid,
+              prfpregset_t *fpregset)
+{
+  struct cleanup *old_chain = save_inferior_pid ();
+
+  inferior_pid = BUILD_LWP (lwpid, main_prochandle.pid);
+  current_target.to_fetch_registers (-1);
+  fill_fpregset (fpregset, -1);
+  do_cleanups (old_chain);
+  return PS_OK;
+}
+
+ps_err_e
+ps_lsetfpregs (gdb_ps_prochandle_t ph,         /* Set LWP float regs */
+              lwpid_t             lwpid,
+              const prfpregset_t *fpregset)
+{
+  struct cleanup *old_chain = save_inferior_pid ();
+
+  inferior_pid = BUILD_LWP (lwpid, main_prochandle.pid);
+  supply_fpregset (fpregset);
+  current_target.to_store_registers (-1);
+  do_cleanups (old_chain);
+  return PS_OK;
+}
+
+/*
+ * ps_getpid
+ *
+ * return the main pid for the child process
+ * (special for Linux -- not used on Solaris)
+ */
+
+pid_t
+ps_getpid (gdb_ps_prochandle_t ph)
+{
+  return ph->pid;
+}
+
+#ifdef TM_I386SOL2_H
+
+/* Reads the local descriptor table of a LWP.  */
+
+ps_err_e
+ps_lgetLDT (gdb_ps_prochandle_t ph, lwpid_t lwpid,
+           struct ssd *pldt)
+{
+  /* NOTE: only used on Solaris, therefore OK to refer to procfs.c */
+  extern struct ssd *procfs_find_LDT_entry (int);
+  struct ssd *ret;
+
+  ret = procfs_find_LDT_entry (BUILD_LWP (lwpid, 
+                                         PIDGET (main_prochandle.pid)));
+  if (ret)
+    {
+      memcpy (pldt, ret, sizeof (struct ssd));
+      return PS_OK;
+    }
+  else /* LDT not found. */
+    return PS_ERR;
+}
+#endif /* TM_I386SOL2_H */
+
+/*
+ * Pointers to thread_db functions:
+ *
+ * These are a dynamic library mechanism.
+ * The dlfcn.h interface will be used to initialize these 
+ * so that they point to the appropriate functions in the
+ * thread_db dynamic library.  This is done dynamically 
+ * so that GDB can still run on systems that lack thread_db.  
+ */
+
+static td_err_e (*p_td_init)              (void);
+
+static td_err_e (*p_td_ta_new)            (const struct ps_prochandle *ph_p, 
+                                          td_thragent_t **ta_pp);
+
+static td_err_e (*p_td_ta_delete)         (td_thragent_t *ta_p);
+
+static td_err_e (*p_td_ta_get_nthreads)   (const td_thragent_t *ta_p,
+                                          int *nthread_p);
+
+
+static td_err_e (*p_td_ta_thr_iter)       (const td_thragent_t *ta_p,
+                                          td_thr_iter_f *cb,
+                                          void *cbdata_p,
+                                          td_thr_state_e state,
+                                          int ti_pri, 
+                                          sigset_t *ti_sigmask_p,
+                                          unsigned ti_user_flags);
+
+static td_err_e (*p_td_ta_event_addr)     (const td_thragent_t *ta_p,
+                                          u_long event,
+                                          td_notify_t *notify_p);
+
+static td_err_e (*p_td_ta_event_getmsg)   (const td_thragent_t *ta_p,
+                                          td_event_msg_t *msg);
+
+static td_err_e (*p_td_ta_set_event)      (const td_thragent_t *ta_p,
+                                          td_thr_events_t *events);
+
+static td_err_e (*p_td_thr_validate)      (const td_thrhandle_t *th_p);
+
+static td_err_e (*p_td_thr_event_enable)  (const td_thrhandle_t *th_p,
+                                          int on_off);
+
+static td_err_e (*p_td_thr_get_info)      (const td_thrhandle_t *th_p,
+                                          td_thrinfo_t *ti_p);
+
+static td_err_e (*p_td_thr_getgregs)      (const td_thrhandle_t *th_p,
+                                          prgregset_t regset);
+
+static td_err_e (*p_td_thr_setgregs)      (const td_thrhandle_t *th_p,
+                                          const prgregset_t regset);
+
+static td_err_e (*p_td_thr_getfpregs)     (const td_thrhandle_t *th_p,
+                                          prfpregset_t *fpregset);
+
+static td_err_e (*p_td_thr_setfpregs)     (const td_thrhandle_t *th_p,
+                                          const prfpregset_t *fpregset);
+
+static td_err_e (*p_td_ta_map_id2thr)     (const td_thragent_t *ta_p,
+                                          thread_t tid,
+                                          td_thrhandle_t *th_p);
+
+static td_err_e (*p_td_ta_map_lwp2thr)    (const td_thragent_t *ta_p,
+                                          lwpid_t lwpid,
+                                          td_thrhandle_t *th_p);
+
+/*
+ * API and target vector initialization function: thread_db_initialize.
+ *
+ * NOTE: this function is deliberately NOT named with the GDB convention
+ * of module initializer function names that begin with "_initialize".
+ * This module is NOT intended to be auto-initialized at GDB startup.
+ * Rather, it will only be initialized when a multi-threaded child
+ * process is detected.
+ *
+ */
+
+/* 
+ * Initializer for thread_db library interface.
+ * This function does the dynamic library stuff (dlopen, dlsym), 
+ * and then calls the thread_db library's one-time initializer 
+ * function (td_init).  If everything succeeds, this function
+ * returns true; otherwise it returns false, and this module
+ * cannot be used.
+ */
+
+static int
+init_thread_db_library ()
+{
+  void *dlhandle;
+  td_err_e ret;
+
+  /* Open a handle to the "thread_db" dynamic library.  */
+  if ((dlhandle = dlopen ("libthread_db.so.1", RTLD_NOW)) == NULL)
+    return 0;                  /* fail */
+
+  /* Initialize pointers to the dynamic library functions we will use.
+   * Note that we are not calling the functions here -- we are only
+   * establishing pointers to them.
+   */
+
+  /* td_init: initialize thread_db library. */
+  if ((p_td_init = dlsym (dlhandle, "td_init")) == NULL)
+    return 0;                  /* fail */
+  /* td_ta_new: register a target process with thread_db.  */
+  if ((p_td_ta_new = dlsym (dlhandle, "td_ta_new")) == NULL)
+    return 0;                  /* fail */
+  /* td_ta_delete: un-register a target process with thread_db.  */
+  if ((p_td_ta_delete = dlsym (dlhandle, "td_ta_delete")) == NULL)
+    return 0;                  /* fail */
+
+  /* td_ta_map_id2thr: get thread handle from thread id.  */
+  if ((p_td_ta_map_id2thr = dlsym (dlhandle, "td_ta_map_id2thr")) == NULL)
+    return 0;                  /* fail */
+  /* td_ta_map_lwp2thr: get thread handle from lwp id.  */
+  if ((p_td_ta_map_lwp2thr = dlsym (dlhandle, "td_ta_map_lwp2thr")) == NULL)
+    return 0;                  /* fail */
+  /* td_ta_get_nthreads: get number of threads in target process.  */
+  if ((p_td_ta_get_nthreads = dlsym (dlhandle, "td_ta_get_nthreads")) == NULL)
+    return 0;                  /* fail */
+  /* td_ta_thr_iter: iterate over all thread handles.  */
+  if ((p_td_ta_thr_iter = dlsym (dlhandle, "td_ta_thr_iter")) == NULL)
+    return 0;                  /* fail */
+
+  /* td_thr_validate: make sure a thread handle is real and alive.  */
+  if ((p_td_thr_validate = dlsym (dlhandle, "td_thr_validate")) == NULL)
+    return 0;                  /* fail */
+  /* td_thr_get_info: get a bunch of info about a thread.  */
+  if ((p_td_thr_get_info = dlsym (dlhandle, "td_thr_get_info")) == NULL)
+    return 0;                  /* fail */
+  /* td_thr_getgregs: get general registers for thread.  */
+  if ((p_td_thr_getgregs = dlsym (dlhandle, "td_thr_getgregs")) == NULL)
+    return 0;                  /* fail */
+  /* td_thr_setgregs: set general registers for thread.  */
+  if ((p_td_thr_setgregs = dlsym (dlhandle, "td_thr_setgregs")) == NULL)
+    return 0;                  /* fail */
+  /* td_thr_getfpregs: get floating point registers for thread.  */
+  if ((p_td_thr_getfpregs = dlsym (dlhandle, "td_thr_getfpregs")) == NULL)
+    return 0;                  /* fail */
+  /* td_thr_setfpregs: set floating point registers for thread.  */
+  if ((p_td_thr_setfpregs = dlsym (dlhandle, "td_thr_setfpregs")) == NULL)
+    return 0;                  /* fail */
+  
+  ret = p_td_init ();
+  if (ret != TD_OK)
+    {
+      warning ("init_thread_db: td_init: %s", thr_err_string (ret));
+      return 0;
+    }
+
+  /* Optional functions:
+     We can still debug even if the following functions are not found.  */
+
+  /* td_ta_event_addr: get the breakpoint address for specified event.  */
+  p_td_ta_event_addr = dlsym (dlhandle, "td_ta_event_addr");
+
+  /* td_ta_event_getmsg: get the next event message for the process.  */
+  p_td_ta_event_getmsg = dlsym (dlhandle, "td_ta_event_getmsg");
+
+  /* td_ta_set_event: request notification of an event.  */
+  p_td_ta_set_event = dlsym (dlhandle, "td_ta_set_event");
+
+  /* td_thr_event_enable: enable event reporting in a thread.  */
+  p_td_thr_event_enable = dlsym (dlhandle, "td_thr_event_enable");
+
+  return 1;                    /* success */
+}
+
+/*
+ * Local utility functions:
+ */
+
+
+/*
+
+   LOCAL FUNCTION
+
+   save_inferior_pid - Save inferior_pid on the cleanup list
+   restore_inferior_pid - Restore inferior_pid from the cleanup list
+
+   SYNOPSIS
+
+   struct cleanup *save_inferior_pid (void);
+   void            restore_inferior_pid (void *saved_pid);
+
+   DESCRIPTION
+
+   These two functions act in unison to restore inferior_pid in
+   case of an error.
+
+   NOTES
+
+   inferior_pid is a global variable that needs to be changed by many
+   of these routines before calling functions in procfs.c.  In order
+   to guarantee that inferior_pid gets restored (in case of errors),
+   you need to call save_inferior_pid before changing it.  At the end
+   of the function, you should invoke do_cleanups to restore it.
+
+ */
+
+static struct cleanup *
+save_inferior_pid (void)
+{
+#if TARGET_PTR_BIT > TARGET_INT_BIT
+  return make_cleanup (restore_inferior_pid, (void *) ((long) inferior_pid));
+#else
+  return make_cleanup (restore_inferior_pid, (void *) inferior_pid);
+#endif
+}
+
+static void
+restore_inferior_pid (void *saved_pid)
+{
+#if TARGET_PTR_BIT > TARGET_INT_BIT
+  inferior_pid = (int) ((long) saved_pid);
+#else
+  inferior_pid = (int) saved_pid;
+#endif
+}
+
+/*
+
+   LOCAL FUNCTION
+
+   thr_err_string - Convert a thread_db error code to a string
+
+   SYNOPSIS
+
+   char * thr_err_string (errcode)
+
+   DESCRIPTION
+
+   Return a string description of the thread_db errcode.  If errcode
+   is unknown, then return an <unknown> message.
+
+ */
+
+static char *
+thr_err_string (errcode)
+     td_err_e errcode;
+{
+  static char buf[50];
+
+  switch (errcode) {
+  case TD_OK:          return "generic 'call succeeded'";
+  case TD_ERR:         return "generic error";
+  case TD_NOTHR:       return "no thread to satisfy query";
+  case TD_NOSV:                return "no sync handle to satisfy query";
+  case TD_NOLWP:       return "no lwp to satisfy query";
+  case TD_BADPH:       return "invalid process handle";
+  case TD_BADTH:       return "invalid thread handle";
+  case TD_BADSH:       return "invalid synchronization handle";
+  case TD_BADTA:       return "invalid thread agent";
+  case TD_BADKEY:      return "invalid key";
+  case TD_NOMSG:       return "no event message for getmsg";
+  case TD_NOFPREGS:    return "FPU register set not available";
+  case TD_NOLIBTHREAD: return "application not linked with libthread";
+  case TD_NOEVENT:     return "requested event is not supported";
+  case TD_NOCAPAB:     return "capability not available";
+  case TD_DBERR:       return "debugger service failed";
+  case TD_NOAPLIC:     return "operation not applicable to";
+  case TD_NOTSD:       return "no thread-specific data for this thread";
+  case TD_MALLOC:      return "malloc failed";
+  case TD_PARTIALREG:  return "only part of register set was written/read";
+  case TD_NOXREGS:     return "X register set not available for this thread";
+  default:
+    sprintf (buf, "unknown thread_db error '%d'", errcode);
+    return buf;
+  }
+}
+
+/*
+
+   LOCAL FUNCTION
+
+   thr_state_string - Convert a thread_db state code to a string
+
+   SYNOPSIS
+
+   char *thr_state_string (statecode)
+
+   DESCRIPTION
+
+   Return the thread_db state string associated with statecode.  
+   If statecode is unknown, then return an <unknown> message.
+
+ */
+
+static char *
+thr_state_string (statecode)
+     td_thr_state_e statecode;
+{
+  static char buf[50];
+
+  switch (statecode) {
+  case TD_THR_STOPPED:         return "stopped by debugger";
+  case TD_THR_RUN:             return "runnable";
+  case TD_THR_ACTIVE:          return "active";
+  case TD_THR_ZOMBIE:          return "zombie";
+  case TD_THR_SLEEP:           return "sleeping";
+  case TD_THR_STOPPED_ASLEEP:  return "stopped by debugger AND blocked";
+  default:
+    sprintf (buf, "unknown thread_db state %d", statecode);
+    return buf;
+  }
+}
+
+/*
+ * Local thread/event list.
+ * This data structure will be used to hold a list of threads and 
+ * pending/deliverable events.
+ */
+
+typedef struct THREADINFO {
+  thread_t       tid;          /* thread ID */
+  pid_t          lid;          /* process/lwp ID */
+  td_thr_state_e state;                /* thread state (a la thread_db) */
+  td_thr_type_e  type;         /* thread type (a la thread_db) */
+  int            pending;      /* true if holding a pending event */
+  int            status;       /* wait status of any interesting event */
+} threadinfo;
+
+threadinfo * threadlist;
+int threadlist_max = 0;                /* current size of table */
+int threadlist_top = 0;                /* number of threads now in table */
+#define THREADLIST_ALLOC 100   /* chunk size by which to expand table */
+
+static threadinfo *
+insert_thread (tid, lid, state, type)
+     int            tid;
+     int            lid;
+     td_thr_state_e state;
+     td_thr_type_e  type;
+{
+  if (threadlist_top >= threadlist_max)
+    {
+      threadlist_max += THREADLIST_ALLOC;
+      threadlist      = realloc (threadlist, 
+                                threadlist_max * sizeof (threadinfo));
+      if (threadlist == NULL)
+       return NULL;
+    }
+  threadlist[threadlist_top].tid     = tid;
+  threadlist[threadlist_top].lid     = lid;
+  threadlist[threadlist_top].state   = state;
+  threadlist[threadlist_top].type    = type;
+  threadlist[threadlist_top].pending = 0;
+  threadlist[threadlist_top].status  = 0;
+
+  return &threadlist[threadlist_top++];
+}
+
+static void
+empty_threadlist ()
+{
+  threadlist_top = 0;
+}
+
+static threadinfo *
+next_pending_event ()
+{
+  int i;
+
+  for (i = 0; i < threadlist_top; i++)
+    if (threadlist[i].pending)
+      return &threadlist[i];
+
+  return NULL;
+}
+
+static void
+threadlist_iter (func, data, state, type)
+     int (*func) ();
+     void *data;
+     td_thr_state_e state;
+     td_thr_type_e  type;
+{
+  int i;
+
+  for (i = 0; i < threadlist_top; i++)
+    if ((state == TD_THR_ANY_STATE || state == threadlist[i].state) &&
+       (type  == TD_THR_ANY_TYPE  || type  == threadlist[i].type))
+      if ((*func) (&threadlist[i], data) != 0)
+       break;
+
+  return;
+}     
+
+/*
+ * Global state
+ * 
+ * Here we keep state information all collected in one place.
+ */
+
+/* This flag is set when we activate, so that we don't do it twice. 
+   Defined in linux-thread.c and used for inter-target syncronization.  */
+extern int using_thread_db;
+
+/* The process id for which we've stopped.
+ * This is only set when we actually stop all threads.
+ * Otherwise it's zero.
+ */
+static int event_pid;
+
+/*
+ * The process id for a new thread to which we've just attached.
+ * This process needs special handling at resume time.
+ */
+static int attach_pid;
+
+
+/*
+ * thread_db event handling:
+ *
+ * The mechanism for event notification via the thread_db API.
+ * These events are implemented as breakpoints.  The thread_db
+ * library gives us an address where we can set a breakpoint.
+ * When the breakpoint is hit, it represents an event of interest
+ * such as:
+ *   Thread creation
+ *   Thread death
+ *   Thread reap
+ */
+
+/* Location of the thread creation event breakpoint.  The code at this
+   location in the child process will be called by the pthread library
+   whenever a new thread is created.  By setting a special breakpoint
+   at this location, GDB can detect when a new thread is created.  We
+   obtain this location via the td_ta_event_addr call.  */
+
+static CORE_ADDR thread_creation_bkpt_address;
+
+/* Location of the thread death event breakpoint.  The code at this
+   location in the child process will be called by the pthread library
+   whenever a thread is destroyed.  By setting a special breakpoint at
+   this location, GDB can detect when a new thread is created.  We
+   obtain this location via the td_ta_event_addr call.  */
+
+static CORE_ADDR thread_death_bkpt_address;
+
+/* This function handles the global parts of enabling thread events.
+   The thread-specific enabling is handled per-thread elsewhere.  */
+
+static void
+enable_thread_event_reporting (ta)
+     td_thragent_t *ta;
+{
+  td_thr_events_t events;
+  td_notify_t     notify;
+  CORE_ADDR       addr;
+
+  if (p_td_ta_set_event     == NULL ||
+      p_td_ta_event_addr    == NULL ||
+      p_td_ta_event_getmsg  == NULL ||
+      p_td_thr_event_enable == NULL)
+    return;    /* can't do thread event reporting without these funcs */
+
+  /* set process wide mask saying which events we are interested in */
+  td_event_emptyset (&events);
+  td_event_addset (&events, TD_CREATE);
+  td_event_addset (&events, TD_DEATH);
+
+  if (p_td_ta_set_event (ta, &events) != TD_OK)
+    {
+      warning ("unable to set global thread event mask");
+      return;
+    }
+
+  /* Delete previous thread event breakpoints, if any.  */
+  remove_thread_event_breakpoints ();
+
+  /* create breakpoints -- thread creation and death */
+  /* thread creation */
+  /* get breakpoint location */
+  if (p_td_ta_event_addr (ta, TD_CREATE, &notify) != TD_OK)
+    {
+      warning ("unable to get location for thread creation breakpoint");
+      return;
+    }
+
+  /* Set up the breakpoint. */
+  create_thread_event_breakpoint (notify.u.bptaddr);
+
+  /* Save it's location. */
+  thread_creation_bkpt_address = notify.u.bptaddr;
+
+  /* thread death */
+  /* get breakpoint location */
+  if (p_td_ta_event_addr (ta, TD_DEATH, &notify) != TD_OK)
+    {
+      warning ("unable to get location for thread death breakpoint");
+      return;
+    }
+  /* Set up the breakpoint. */
+  create_thread_event_breakpoint (notify.u.bptaddr);
+
+  /* Save it's location. */
+  thread_death_bkpt_address = notify.u.bptaddr;
+}
+
+/* This function handles the global parts of disabling thread events.
+   The thread-specific enabling is handled per-thread elsewhere.  */
+
+static void
+disable_thread_event_reporting (ta)
+     td_thragent_t *ta;
+{
+  td_thr_events_t events;
+
+  /* set process wide mask saying we aren't interested in any events */
+  td_event_emptyset (&events);
+  p_td_ta_set_event (main_threadagent, &events);
+
+  /* Delete thread event breakpoints, if any.  */
+  remove_thread_event_breakpoints ();
+  thread_creation_bkpt_address = 0;
+  thread_death_bkpt_address = 0;
+}
+
+/* check_for_thread_event
+   
+   if it's a thread event we recognize (currently
+   we only recognize creation and destruction
+   events), return 1; else return 0.  */
+
+
+static int
+check_for_thread_event (struct target_waitstatus *tws, int event_pid)
+{
+  /* FIXME: to be more efficient, we should keep a static 
+     list of threads, and update it only here (with td_ta_thr_iter). */
+}
+
+static void
+thread_db_push_target (void)
+{
+  /* Called ONLY from thread_db_new_objfile after td_ta_new call succeeds. */
+
+  /* Push this target vector */
+  push_target (&thread_db_ops);
+  /* Find the underlying process-layer target for calling later.  */
+  target_beneath = find_target_beneath (&thread_db_ops);
+  using_thread_db = 1;
+  /* Turn on thread_db event-reporting API.  */
+  enable_thread_event_reporting (main_threadagent);
+}
+
+static void
+thread_db_unpush_target (void)
+{
+  /* Must be called whenever we remove ourself from the target stack! */
+
+  using_thread_db = 0;
+  target_beneath = NULL;
+
+  /* delete local list of threads */
+  empty_threadlist ();
+  /* Turn off the thread_db API.  */
+  p_td_ta_delete (main_threadagent);
+  /* Unpush this target vector */
+  unpush_target (&thread_db_ops);
+  /* Reset linuxthreads module.  */
+  linuxthreads_discard_global_state ();
+}
+
+/*
+ * New objfile hook function:
+ * Called for each new objfile (image, shared lib) in the target process.
+ *
+ * The purpose of this function is to detect that the target process
+ * is linked with the (appropriate) thread library.  So every time a
+ * new target shared library is detected, we will call td_ta_new.
+ * If it succeeds, we know we have a multi-threaded target process
+ * that we can debug using the thread_db API.
+ */
+
+/* 
+ * new_objfile function:
+ *
+ * connected to target_new_objfile_hook, this function gets called
+ * every time a new binary image is loaded.
+ *
+ * At each call, we attempt to open the thread_db connection to the
+ * child process.  If it succeeds, we know we have a libthread process
+ * and we can debug it with this target vector.  Therefore we push
+ * ourself onto the target stack.
+ */
+
+static void (*target_new_objfile_chain)   (struct objfile *objfile);
+static int stop_or_attach_thread_callback (const td_thrhandle_t *th, 
+                                          void *data);
+static int wait_thread_callback           (const td_thrhandle_t *th, 
+                                          void *data);
+
+static void
+thread_db_new_objfile (struct objfile *objfile)
+{
+  td_err_e   ret;
+  
+  if (using_thread_db)                 /* libthread already detected, and */
+    goto quit;                         /* thread target vector activated. */
+
+  if (objfile == NULL)
+    goto quit; /* un-interesting object file */
+
+  /* Initialize our "main prochandle" with the main inferior pid.  */
+  main_prochandle.pid = PIDGET (inferior_pid);
+
+  /* Now attempt to open a thread_db connection to the 
+     thread library running in the child process.  */
+  ret = p_td_ta_new (&main_prochandle, &main_threadagent);
+  switch (ret) {
+  default:
+    warning ("Unexpected error initializing thread_db: %s", 
+            thr_err_string (ret));
+    break;
+  case TD_NOLIBTHREAD: /* expected: no libthread in child process (yet) */
+    break;     
+  case TD_OK:          /* libthread detected in child: we go live now! */
+    thread_db_push_target ();
+    event_pid = inferior_pid;  /* for resume */
+
+    /* Now stop everyone else, and attach any new threads you find.  */
+    p_td_ta_thr_iter (main_threadagent, 
+                     stop_or_attach_thread_callback,
+                     (void *) 0,
+                     TD_THR_ANY_STATE,
+                     TD_THR_LOWEST_PRIORITY,
+                     TD_SIGNO_MASK,
+                     TD_THR_ANY_USER_FLAGS);
+
+    /* Now go call wait on all the threads you've stopped:
+       This allows us to absorb the SIGKILL event, and to make sure
+       that the thread knows that it is stopped (Linux peculiarity).  */
+    p_td_ta_thr_iter (main_threadagent, 
+                     wait_thread_callback,
+                     (void *) 0,
+                     TD_THR_ANY_STATE,
+                     TD_THR_LOWEST_PRIORITY,
+                     TD_SIGNO_MASK,
+                     TD_THR_ANY_USER_FLAGS);
+
+    break;
+  }
+quit:
+  if (target_new_objfile_chain)
+    target_new_objfile_chain (objfile);
+}
+
+
+/* 
+
+   LOCAL FUNCTION
+
+   thread_db_alive     - test thread for "aliveness"
+
+   SYNOPSIS
+
+   static bool thread_db_alive (int pid);
+
+   DESCRIPTION
+
+   returns true if thread still active in inferior.
+
+ */
+
+static int
+thread_db_alive (pid)
+     int pid;
+{
+  if (is_thread (pid))         /* user-space (non-kernel) thread */
+    {
+      td_thrhandle_t th;
+      td_err_e ret;
+
+      pid = GET_THREAD (pid);
+      if ((ret = p_td_ta_map_id2thr (main_threadagent, pid, &th)) != TD_OK)
+       return 0;               /* thread not found */
+      if ((ret = p_td_thr_validate (&th)) != TD_OK)
+       return 0;               /* thread not valid */
+      return 1;                        /* known thread: return true */
+    }
+  else if (target_beneath->to_thread_alive)
+    return target_beneath->to_thread_alive (pid);
+  else
+    return 0;          /* default to "not alive" (shouldn't happen anyway) */
+}
+
+/*
+ * get_lwp_from_thread_handle
+ */
+
+static int     /* lwpid_t or pid_t */
+get_lwp_from_thread_handle (th)
+     td_thrhandle_t *th;
+{
+  td_thrinfo_t ti;
+  td_err_e     ret;
+
+  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+    error ("get_lwp_from_thread_handle: thr_get_info failed: %s", 
+          thr_err_string (ret));
+
+  return ti.ti_lid;
+}
+
+/*
+ * get_lwp_from_thread_id
+ */
+
+static int     /* lwpid_t or pid_t */
+get_lwp_from_thread_id (tid)
+     int tid;  /* thread_t? */
+{
+  td_thrhandle_t th;
+  td_err_e       ret;
+
+  if ((ret = p_td_ta_map_id2thr (main_threadagent, tid, &th)) != TD_OK)
+    error ("get_lwp_from_thread_id: map_id2thr failed: %s", 
+          thr_err_string (ret));
+
+  return get_lwp_from_thread_handle (&th);
+}
+
+/* 
+ * pid_to_str has to handle user-space threads.
+ * If not a user-space thread, then pass the request on to the 
+ * underlying stratum if it can handle it: else call normal_pid_to_str.
+ */
+
+static char *
+thread_db_pid_to_str (int pid)
+{
+  static char buf[100];
+  td_thrhandle_t th;
+  td_thrinfo_t ti;
+  td_err_e ret;
+
+  if (is_thread (pid))
+    {
+      if ((ret = p_td_ta_map_id2thr (main_threadagent, 
+                                    GET_THREAD (pid),
+                                    &th)) != TD_OK)
+       error ("thread_db: map_id2thr failed: %s", thr_err_string (ret));
+
+      if ((ret = p_td_thr_get_info (&th, &ti)) != TD_OK)
+       error ("thread_db: thr_get_info failed: %s", thr_err_string (ret));
+
+      if (ti.ti_state == TD_THR_ACTIVE &&
+         ti.ti_lid != 0)
+       sprintf (buf, "Thread %d (LWP %d)", ti.ti_tid, ti.ti_lid);
+      else
+       sprintf (buf, "Thread %d (%s)", ti.ti_tid,
+                thr_state_string (ti.ti_state));
+    }
+  else if (GET_LWP (pid))
+    sprintf (buf, "LWP %d", GET_LWP (pid));
+  else return normal_pid_to_str (pid);
+
+  return buf;
+}
+
+/* 
+ * thread_db target vector functions:
+ */
+
+static void
+thread_db_files_info (struct target_ops *tgt_vector)
+{
+  /* This function will be unnecessary in real life.  */
+  printf_filtered ("thread_db stratum:\n");
+  target_beneath->to_files_info (tgt_vector);
+}
+
+/* 
+ * xfer_memory has to munge the inferior_pid before passing the call
+ * down to the target layer.  
+ */
+
+static int
+thread_db_xfer_memory (memaddr, myaddr, len, dowrite, target)
+     CORE_ADDR memaddr;
+     char *myaddr;
+     int len;
+     int dowrite;
+     struct target_ops *target;        /* ignored */
+{
+  struct cleanup *old_chain;
+  int ret;
+
+  old_chain = save_inferior_pid ();
+
+  if (is_thread (inferior_pid) ||
+      !target_thread_alive (inferior_pid))
+    {
+      /* FIXME: use the LID/LWP, so that underlying process layer
+        can read memory from specific threads?  */
+      inferior_pid = main_prochandle.pid;
+    }
+
+  ret = target_beneath->to_xfer_memory (memaddr, myaddr, len,
+                                          dowrite, target);
+  do_cleanups (old_chain);
+  return ret;
+}
+
+/* 
+ * fetch_registers has to determine if inferior_pid is a user-space thread.
+ * If so, we use the thread_db API to get the registers.
+ * And if not, we call the underlying process stratum.
+ */
+
+static void
+thread_db_fetch_registers (regno)
+     int regno;
+{
+  td_thrhandle_t thandle;
+  prfpregset_t fpregset;
+  prgregset_t gregset;
+  thread_t thread;
+  td_err_e ret;
+
+  if (!is_thread (inferior_pid))       /* kernel thread */
+    {                  /* pass the request on to the target underneath.  */
+      target_beneath->to_fetch_registers (regno);
+      return;
+    }
+
+  /* convert inferior_pid into a td_thrhandle_t */
+
+  if ((thread = GET_THREAD (inferior_pid)) == 0)
+    error ("fetch_registers:  thread == 0");
+
+  if ((ret = p_td_ta_map_id2thr (main_threadagent, thread, &thandle)) != TD_OK)
+    error ("fetch_registers: td_ta_map_id2thr: %s", thr_err_string (ret));
+
+  /* Get the integer regs: 
+     For the sparc, TD_PARTIALREG means that only i0->i7, l0->l7, 
+     pc and sp are saved (by a thread context switch).  */
+  if ((ret = p_td_thr_getgregs (&thandle, gregset)) != TD_OK &&
+      ret != TD_PARTIALREG)
+    error ("fetch_registers: td_thr_getgregs %s", thr_err_string (ret));
+
+  /* And, now the fp regs */
+  if ((ret = p_td_thr_getfpregs (&thandle, &fpregset)) != TD_OK &&
+      ret != TD_NOFPREGS)
+    error ("fetch_registers: td_thr_getfpregs %s", thr_err_string (ret));
+
+/* Note that we must call supply_{g fp}regset *after* calling the td routines
+   because the td routines call ps_lget* which affect the values stored in the
+   registers array.  */
+
+  supply_gregset (gregset);
+  supply_fpregset (&fpregset);
+
+}
+
+/* 
+ * store_registers has to determine if inferior_pid is a user-space thread.
+ * If so, we use the thread_db API to get the registers.
+ * And if not, we call the underlying process stratum.
+ */
+
+static void
+thread_db_store_registers (regno)
+     int regno;
+{
+  td_thrhandle_t thandle;
+  prfpregset_t fpregset;
+  prgregset_t  gregset;
+  thread_t thread;
+  td_err_e ret;
+
+  if (!is_thread (inferior_pid))       /* Kernel thread: */
+    {          /* pass the request on to the underlying target vector.  */
+      target_beneath->to_store_registers (regno);
+      return;
+    }
+
+  /* convert inferior_pid into a td_thrhandle_t */
+
+  if ((thread = GET_THREAD (inferior_pid)) == 0)
+    error ("store_registers: thread == 0");
+
+  if ((ret = p_td_ta_map_id2thr (main_threadagent, thread, &thandle)) != TD_OK)
+    error ("store_registers: td_ta_map_id2thr %s", thr_err_string (ret));
+
+  if (regno != -1)
+    {                          /* Not writing all the regs */
+      /* save new register value */
+      /* MVS: I don't understand this... */
+      char old_value[REGISTER_SIZE];
+
+      memcpy (old_value, &registers[REGISTER_BYTE (regno)], REGISTER_SIZE);
+
+      if ((ret = p_td_thr_getgregs (&thandle, gregset)) != TD_OK)
+       error ("store_registers: td_thr_getgregs %s", thr_err_string (ret));
+      if ((ret = p_td_thr_getfpregs (&thandle, &fpregset)) != TD_OK)
+       error ("store_registers: td_thr_getfpregs %s", thr_err_string (ret));
+
+      /* restore new register value */
+      memcpy (&registers[REGISTER_BYTE (regno)], old_value, REGISTER_SIZE);
+
+    }
+
+  fill_gregset  (gregset, regno);
+  fill_fpregset (&fpregset, regno);
+
+  if ((ret = p_td_thr_setgregs (&thandle, gregset)) != TD_OK)
+    error ("store_registers: td_thr_setgregs %s", thr_err_string (ret));
+  if ((ret = p_td_thr_setfpregs (&thandle, &fpregset)) != TD_OK &&
+      ret != TD_NOFPREGS)
+    error ("store_registers: td_thr_setfpregs %s", thr_err_string (ret));
+}
+
+static void
+handle_new_thread (tid, lid, verbose)
+     int tid;  /* user thread id */
+     int lid;  /* kernel thread id */
+     int verbose;
+{
+  int gdb_pid = BUILD_THREAD (tid, main_prochandle.pid);
+  int wait_pid, wait_status;
+
+  if (verbose)
+    printf_filtered ("[New %s]\n", target_pid_to_str (gdb_pid));
+  add_thread (gdb_pid);
+
+  if (lid != main_prochandle.pid)
+    {
+      attach_thread (lid);
+      /* According to the Eric Paire model, we now have to send
+        the restart signal to the new thread -- however, empirically,
+        I do not find that to be necessary.  */
+      attach_pid = lid;
+    }
+}
+
+static void
+test_for_new_thread (tid, lid, verbose)
+     int tid;
+     int lid;
+     int verbose;
+{
+  if (!in_thread_list (BUILD_THREAD (tid, main_prochandle.pid)))
+    handle_new_thread (tid, lid, verbose);
+}
+
+/* 
+ * Callback function that gets called once per USER thread 
+ * (i.e., not kernel) thread by td_ta_thr_iter.
+ */
+
+static int
+find_new_threads_callback (th, ignored)
+     const td_thrhandle_t *th;
+     void *ignored;
+{
+  td_thrinfo_t ti;
+  td_err_e     ret;
+
+  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+    {
+      warning ("find_new_threads_callback: %s", thr_err_string (ret));
+      return -1;               /* bail out, get_info failed. */
+    }
+
+  /* FIXME: 
+     As things now stand, this should never detect a new thread.
+     But if it does, we could be in trouble because we aren't calling
+     wait_thread_callback for it.  */
+  test_for_new_thread (ti.ti_tid, ti.ti_lid, 0);
+  return 0;
+}
+
+/* 
+ * find_new_threads uses the thread_db iterator function to discover
+ * user-space threads.  Then if the underlying process stratum has a
+ * find_new_threads method, we call that too.
+ */
+
+static void
+thread_db_find_new_threads ()
+{
+  if (inferior_pid == -1)      /* FIXME: still necessary? */
+    {
+      printf_filtered ("No process.\n");
+      return;
+    }
+  p_td_ta_thr_iter (main_threadagent, 
+                   find_new_threads_callback, 
+                   (void *) 0, 
+                   TD_THR_ANY_STATE, 
+                   TD_THR_LOWEST_PRIORITY,
+                   TD_SIGNO_MASK, 
+                   TD_THR_ANY_USER_FLAGS);
+  if (target_beneath->to_find_new_threads)
+    target_beneath->to_find_new_threads ();
+}
+
+/*
+ * Resume all threads, or resume a single thread.
+ * If step is true, then single-step the appropriate thread
+ * (or single-step inferior_pid, but continue everyone else).
+ * If signo is true, then send that signal to at least one thread.
+ */
+
+/*
+ * This function is called once for each thread before resuming.
+ * It sends continue (no step, and no signal) to each thread except
+ *   the main thread, and
+ *   the event thread (the one that stopped at a breakpoint etc.)
+ *
+ * The event thread is handled separately so that it can be sent
+ * the stepping and signal args with which target_resume was called.
+ *
+ * The main thread is resumed last, so that the thread_db proc_service
+ * callbacks will still work during the iterator function.
+ */
+
+static int
+resume_thread_callback (th, data)
+     const td_thrhandle_t *th;
+     void *data;
+{
+  td_thrinfo_t ti;
+  td_err_e     ret;
+
+  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+    {
+      warning ("resume_thread_callback: %s", thr_err_string (ret));
+      return -1;               /* bail out, get_info failed. */
+    }
+  /* FIXME: 
+     As things now stand, this should never detect a new thread.
+     But if it does, we could be in trouble because we aren't calling
+     wait_thread_callback for it.  */
+  test_for_new_thread (ti.ti_tid, ti.ti_lid, 1);
+
+  if (ti.ti_lid != main_prochandle.pid &&
+      ti.ti_lid != event_pid)
+    {
+      /* Unconditionally continue the thread with no signal.
+        Only the event thread will get a signal of any kind.  */
+
+      target_beneath->to_resume (ti.ti_lid, 0, 0);
+    }
+  return 0;
+}
+
+static int
+new_resume_thread_callback (thread, data)
+     threadinfo *thread;
+     void *data;
+{
+  if (thread->lid != event_pid &&
+      thread->lid != main_prochandle.pid)
+    {
+      /* Unconditionally continue the thread with no signal (for now).  */
+
+      target_beneath->to_resume (thread->lid, 0, 0);
+    }
+  return 0;
+}
+
+static int last_resume_pid;
+static int last_resume_step;
+static int last_resume_signo;
+
+static void
+thread_db_resume (pid, step, signo)
+     int pid;
+     int step;
+     enum target_signal signo;
+{
+  last_resume_pid   = pid;
+  last_resume_step  = step;
+  last_resume_signo = signo;
+
+  /* resuming a specific pid? */
+  if (pid != -1)
+    {
+      if (is_thread (pid))
+       pid = get_lwp_from_thread_id (GET_THREAD (pid));
+      else if (GET_LWP (pid))
+       pid = GET_LWP (pid);
+    }
+
+  /* Apparently the interpretation of 'pid' is dependent on 'step':
+     If step is true, then a specific pid means 'step only this pid'.
+     But if step is not true, then pid means 'continue ALL pids, but
+     give the signal only to this one'.  */
+  if (pid != -1 && step)
+    {
+      /* FIXME: is this gonna work in all circumstances? */
+      target_beneath->to_resume (pid, step, signo);
+    }
+  else
+    {
+      /* 1) Continue all threads except the event thread and the main thread.
+        2) resume the event thread with step and signo.
+        3) If event thread != main thread, continue the main thread.
+
+        Note: order of 2 and 3 may need to be reversed.  */
+
+      threadlist_iter (new_resume_thread_callback, 
+                       (void *) 0, 
+                       TD_THR_ANY_STATE, 
+                       TD_THR_ANY_TYPE);
+      /* now resume event thread, and if necessary also main thread. */
+      if (event_pid)
+       {
+         target_beneath->to_resume (event_pid, step, signo);
+       }
+      if (event_pid != main_prochandle.pid)
+       {
+         target_beneath->to_resume (main_prochandle.pid, 0, 0);
+       }
+    }
+}
+
+/* All new threads will be attached.
+   All previously known threads will be stopped using kill (SIGKILL).  */
+
+static int
+stop_or_attach_thread_callback (const td_thrhandle_t *th, void *data)
+{
+  td_thrinfo_t ti;
+  td_err_e     ret;
+  int          gdb_pid;
+  int on_off = 1;
+
+  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+    {
+      warning ("stop_or_attach_thread_callback: %s", thr_err_string (ret));
+      return -1;               /* bail out, get_info failed. */
+    }
+
+  /* First add it to our internal list.  
+     We build this list anew at every wait event.  */
+  insert_thread (ti.ti_tid, ti.ti_lid, ti.ti_state, ti.ti_type);
+  /* Now: if we've already seen it, stop it, else add it and attach it.  */
+  gdb_pid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
+  if (!in_thread_list (gdb_pid))       /* new thread */
+    {
+      handle_new_thread (ti.ti_tid, ti.ti_lid, 1);
+      /* Enable thread events */
+      if (p_td_thr_event_enable)
+       if ((ret = p_td_thr_event_enable (th, on_off)) != TD_OK)
+         warning ("stop_or_attach_thread: %s", thr_err_string (ret));
+    }
+  else if (ti.ti_lid != event_pid &&
+          ti.ti_lid != main_prochandle.pid)
+    {
+      ret = (td_err_e) kill (ti.ti_lid, SIGSTOP);
+    }
+
+  return 0;
+}
+     
+/*
+ * Wait for signal N from pid PID.
+ * If wait returns any other signals, put them back before returning.
+ */
+
+static void
+wait_for_stop (pid)
+     int pid;
+{
+  int i;
+  int retpid;
+  int status;
+
+  /* Array of wait/signal status */
+  /* FIXME: wrong data structure, we need a queue.
+     Realtime signals may be delivered more than once.  
+     And at that, we really can't handle them (see below).  */
+#if defined (NSIG)
+  static int   wstatus [NSIG];
+#elif defined (_NSIG)
+  static int   wstatus [_NSIG];
+#else
+#error No definition for number of signals!
+#endif
+
+  /* clear wait/status list */
+  memset (&wstatus, 0, sizeof (wstatus));
+
+  /* Now look for SIGSTOP event on all threads except event thread.  */
+  do {
+    errno = 0;
+    if (pid == main_prochandle.pid)
+      retpid = waitpid (pid, &status, 0);
+    else
+      retpid = waitpid (pid, &status, __WCLONE);
+
+    if (retpid > 0)
+      if (WSTOPSIG (status) == SIGSTOP)
+       {
+         /* Got the SIGSTOP event we're looking for.
+            Throw it away, and throw any other events back!  */
+         for (i = 0; i < sizeof(wstatus) / sizeof (wstatus[0]); i++)
+           if (wstatus[i])
+             if (i != SIGSTOP)
+               {
+                 kill (retpid, i);
+               }
+         break;        /* all done */
+       }
+      else
+       {
+         int signo;
+         /* Oops, got an event other than SIGSTOP.
+            Save it, and throw it back after we find the SIGSTOP event.  */
+         
+         /* FIXME (how?)  This method is going to fail for realtime
+            signals, which cannot be put back simply by using kill.  */
+
+         if (WIFEXITED (status))
+           error ("Ack!  Thread Exited event.  What do I do now???");
+         else if (WIFSTOPPED (status))
+           signo = WSTOPSIG (status);
+         else
+           signo = WTERMSIG (status);
+         
+         /* If a thread other than the event thread has hit a GDB
+            breakpoint (as opposed to some random trap signal), then
+            just arrange for it to hit it again later.  Back up the
+            PC if necessary.  Don't forward the SIGTRAP signal to
+            the thread.  We will handle the current event, eventually
+            we will resume all the threads, and this one will get
+            it's breakpoint trap again.
+
+            If we do not do this, then we run the risk that the user
+            will delete or disable the breakpoint, but the thread will
+            have already tripped on it.  */
+
+         if (retpid != event_pid &&
+             signo == SIGTRAP &&
+             breakpoint_inserted_here_p (read_pc_pid (retpid) - 
+                                         DECR_PC_AFTER_BREAK))
+           {
+             /* Set the pc to before the trap and DO NOT re-send the signal */
+             if (DECR_PC_AFTER_BREAK)
+               write_pc_pid (read_pc_pid (retpid) - DECR_PC_AFTER_BREAK,
+                             retpid);
+           }
+
+         /* Since SIGINT gets forwarded to the entire process group
+            (in the case where ^C is typed at the tty / console), 
+            just ignore all SIGINTs from other than the event thread.  */
+         else if (retpid != event_pid && signo == SIGINT)
+           { /* do nothing.  Signal will disappear into oblivion!  */
+             ;
+           }
+
+         else /* This is some random signal other than a breakpoint. */
+           {
+             wstatus [signo] = 1;
+           }
+         child_resume (retpid, 0, TARGET_SIGNAL_0);
+         continue;
+       }
+
+  } while (errno == 0 || errno == EINTR);
+}
+
+/*
+ * wait_thread_callback
+ *
+ * Calls waitpid for each thread, repeatedly if necessary, until
+ * SIGSTOP is returned.  Afterward, if any other signals were returned
+ * by waitpid, return them to the thread's pending queue by calling kill.
+ */
+
+static int
+wait_thread_callback (const td_thrhandle_t *th, void *data)
+{
+  td_thrinfo_t ti;
+  td_err_e     ret;
+
+  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+    {
+      warning ("wait_thread_callback: %s", thr_err_string (ret));
+      return -1;               /* bail out, get_info failed. */
+    }
+
+  /* This callback to act on all threads except the event thread: */
+  if (ti.ti_lid == event_pid ||        /* no need to wait (no sigstop) */
+      ti.ti_lid == main_prochandle.pid)        /* no need to wait (already waited) */
+    return 0;  /* don't wait on the event thread.  */
+
+  wait_for_stop (ti.ti_lid);
+  return 0;    /* finished: next thread. */
+}
+
+static int
+new_wait_thread_callback (thread, data)
+     threadinfo *thread;
+     void *data;
+{
+  /* don't wait on the event thread -- it's already stopped and waited.  
+     Ditto the main thread.  */
+  if (thread->lid != event_pid &&
+      thread->lid != main_prochandle.pid)
+    {
+      wait_for_stop (thread->lid);
+    }
+  return 0;
+}
+
+/* 
+ * Wait for any thread to stop, by calling the underlying wait method.
+ * The PID returned by the underlying target may be a kernel thread,
+ * in which case we will want to convert it to the corresponding
+ * user-space thread.  
+ */
+
+static int
+thread_db_wait (int pid, struct target_waitstatus *ourstatus)
+{
+  td_thrhandle_t thandle;
+  td_thrinfo_t ti;
+  td_err_e ret;
+  lwpid_t lwp;
+  int retpid;
+  int status;
+  int save_errno;
+
+  /* OK, we're about to wait for an event from the running inferior.
+     Make sure we're ignoring the right signals.  */
+
+  check_all_signal_numbers (); /* see if magic signals changed. */
+
+  event_pid = 0;
+  attach_pid = 0;
+
+  /* FIXME: should I do the wait right here inline?  */
+#if 0
+  if (pid == -1)
+    lwp = -1;
+  else
+    lwp = get_lwp_from_thread_id (GET_THREAD (pid));
+#endif
+
+
+  save_errno = linux_child_wait (-1, &retpid, &status);
+  store_waitstatus (ourstatus, status);
+
+  /* Thread ID is irrelevant if the target process exited.
+     FIXME: do I have any killing to do?
+     Can I get this event mistakenly from a thread?  */
+  if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+    return retpid;
+
+  /* OK, we got an event of interest.
+     Go stop all threads and look for new ones.
+     FIXME: maybe don't do this for the restart signal?  Optimization...  */
+  event_pid = retpid;
+
+  /* If the last call to resume was for a specific thread, then we don't
+     need to stop everyone else: they should already be stopped.  */
+  if (last_resume_step == 0 || last_resume_pid == -1)
+    {
+      /* Main thread must be stopped before calling the iterator.  */
+      if (retpid != main_prochandle.pid)
+       {
+         kill (main_prochandle.pid, SIGSTOP);
+         wait_for_stop (main_prochandle.pid);
+       }
+
+      empty_threadlist ();
+      /* Now stop everyone else, and attach any new threads you find.  */
+      p_td_ta_thr_iter (main_threadagent, 
+                       stop_or_attach_thread_callback,
+                       (void *) 0,
+                       TD_THR_ANY_STATE,
+                       TD_THR_LOWEST_PRIORITY,
+                       TD_SIGNO_MASK,
+                       TD_THR_ANY_USER_FLAGS);
+
+      /* Now go call wait on all the threads we've stopped:
+        This allows us to absorb the SIGKILL event, and to make sure
+        that the thread knows that it is stopped (Linux peculiarity).  */
+
+      threadlist_iter (new_wait_thread_callback, 
+                      (void *) 0,
+                      TD_THR_ANY_STATE, 
+                      TD_THR_ANY_TYPE);
+    }
+
+  /* Convert the kernel thread id to the corresponding thread id.  */
+
+  /* If the process layer does not furnish an lwp,
+     then perhaps the returned pid IS the lwp... */
+  if ((lwp = GET_LWP (retpid)) == 0)
+    lwp = retpid;
+
+  if ((ret = p_td_ta_map_lwp2thr (main_threadagent, lwp, &thandle)) != TD_OK)
+    return retpid;     /* LWP is not mapped onto a user-space thread. */
+
+  if ((ret = p_td_thr_validate (&thandle)) != TD_OK)
+    return retpid;     /* LWP is not mapped onto a valid thread. */
+
+  if ((ret = p_td_thr_get_info (&thandle, &ti)) != TD_OK)
+    {
+      warning ("thread_db: thr_get_info failed ('%s')", thr_err_string (ret));
+      return retpid;
+    }
+
+  retpid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
+  /* If this is a new user thread, notify GDB about it.  */
+  if (!in_thread_list (retpid))
+    {
+      printf_filtered ("[New %s]\n", target_pid_to_str (retpid));
+      add_thread (retpid);
+    }
+
+#if 0
+  /* Now detect if this is a thread creation/deletion event: */
+  check_for_thread_event (ourstatus, retpid);
+#endif
+  return retpid;
+}
+
+/* 
+ * kill has to call the underlying kill.
+ * FIXME: I'm not sure if it's necessary to check inferior_pid any more,
+ * but we might need to fix inferior_pid up if it's a user thread.
+ */
+
+static int
+kill_thread_callback (th, data)
+     td_thrhandle_t *th;
+     void *data;
+{
+  td_thrinfo_t ti;
+  td_err_e     ret;
+
+  /* Fixme: 
+     For Linux, threads may need to be waited.  */
+  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+    {
+      warning ("kill_thread_callback: %s", thr_err_string (ret));
+      return -1;               /* bail out, get_info failed. */
+    }
+
+  if (ti.ti_lid != main_prochandle.pid)
+    {
+      kill (ti.ti_lid, SIGKILL);
+    }
+  return 0;
+}
+
+
+static void thread_db_kill (void)
+{
+  int rpid;
+  int status;
+
+  /* Fixme: 
+     For Linux, threads may need to be waited.  */
+  if (inferior_pid != 0)
+    {
+      /* Go kill the children first.  Save the main thread for last. */
+      p_td_ta_thr_iter (main_threadagent, 
+                       kill_thread_callback, 
+                       (void *) 0, 
+                       TD_THR_ANY_STATE,
+                       TD_THR_LOWEST_PRIORITY,
+                       TD_SIGNO_MASK,
+                       TD_THR_ANY_USER_FLAGS);
+
+      /* Turn off thread_db event-reporting API *before* killing the
+        main thread, since this operation requires child memory access.
+        Can't move this into thread_db_unpush target because then 
+        detach would not work.  */
+      disable_thread_event_reporting (main_threadagent);
+
+      inferior_pid = main_prochandle.pid;
+
+      /* 
+       * Since both procfs_kill and ptrace_kill call target_mourn, 
+       * it should be sufficient for me to call one of them.
+       * That will result in my mourn being called, which will both
+       * unpush me and call the underlying mourn.
+       */
+      target_beneath->to_kill ();
+    }
+
+  /* Wait for all threads. */
+  /* FIXME: need a universal wait_for_signal func? */
+  do
+    {
+      rpid = waitpid (-1, &status, __WCLONE | WNOHANG);
+    }
+  while (rpid > 0 || errno == EINTR);
+
+  do
+    {
+      rpid = waitpid (-1, &status, WNOHANG);
+    }
+  while (rpid > 0 || errno == EINTR);
+}
+
+/* 
+ * Mourn has to remove us from the target stack, 
+ * and then call the underlying mourn.
+ */
+
+static void thread_db_mourn_inferior (void)
+{
+  thread_db_unpush_target ();
+  target_mourn_inferior ();    /* call the underlying mourn */
+}
+
+/* 
+ * Detach has to remove us from the target stack, 
+ * and then call the underlying detach.
+ *
+ * But first, it has to detach all the cloned threads!
+ */
+
+static int
+detach_thread_callback (th, data)
+     td_thrhandle_t *th;
+     void *data;
+{
+  /* Called once per thread.  */
+  td_thrinfo_t ti;
+  td_err_e     ret;
+
+  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+    {
+      warning ("detach_thread_callback: %s", thr_err_string (ret));
+      return -1;                /* bail out, get_info failed. */
+    }
+
+  if (!in_thread_list (BUILD_THREAD (ti.ti_tid, main_prochandle.pid)))
+    return 0;  /* apparently we don't know this one.  */
+
+  /* Save main thread for last, or the iterator will fail! */
+  if (ti.ti_lid != main_prochandle.pid)
+    {
+      struct cleanup *old_chain;
+      int off = 0;
+
+      /* Time to detach this thread. 
+        First disable thread_db event reporting for the thread.  */
+      if (p_td_thr_event_enable &&
+         (ret = p_td_thr_event_enable (th, off)) != TD_OK)
+       {
+         warning ("detach_thread_callback: %s\n", thr_err_string (ret));
+         return 0;
+       }
+
+      /* Now cancel any pending SIGTRAPS.  FIXME!  */
+
+      /* Call underlying detach method.  FIXME just detach it.  */
+      old_chain = save_inferior_pid ();
+      inferior_pid = ti.ti_lid;
+      detach (TARGET_SIGNAL_0);
+      do_cleanups (old_chain);
+    }
+  return 0;
+}
+
+static void
+thread_db_detach (char *args, int from_tty)
+{
+  td_err_e ret;
+
+  if ((ret = p_td_ta_thr_iter (main_threadagent, 
+                              detach_thread_callback, 
+                              (void *) 0, 
+                              TD_THR_ANY_STATE,
+                              TD_THR_LOWEST_PRIORITY,
+                              TD_SIGNO_MASK,
+                              TD_THR_ANY_USER_FLAGS))
+      != TD_OK)
+    warning ("detach (thr_iter): %s", thr_err_string (ret));
+
+  /* Turn off thread_db event-reporting API 
+     (before detaching the main thread) */
+  disable_thread_event_reporting (main_threadagent);
+
+  thread_db_unpush_target ();
+
+  /* above call nullifies target_beneath, so don't use that! */
+  inferior_pid = PIDGET (inferior_pid);
+  target_detach (args, from_tty);
+}
+
+
+/* 
+ * We never want to actually create the inferior!
+ *
+ * If this is ever called, it means we were on the target stack
+ * when the user said "run".  But we don't want to be on the new
+ * inferior's target stack until the thread_db / libthread
+ * connection is ready to be made.
+ *
+ * So, what shall we do?
+ * Unpush ourselves from the stack, and then invoke
+ * find_default_create_inferior, which will invoke the
+ * appropriate process_stratum target to do the create.
+ */
+
+static void
+thread_db_create_inferior (exec_file, allargs, env)
+     char *exec_file;
+     char *allargs;
+     char **env;
+{
+  thread_db_unpush_target ();
+  find_default_create_inferior (exec_file, allargs, env);
+}
+
+/* 
+ * Thread_db target vector initializer.
+ */
+
+void
+init_thread_db_ops ()
+{
+  thread_db_ops.to_shortname        = "multi-thread";
+  thread_db_ops.to_longname         = "multi-threaded child process.";
+  thread_db_ops.to_doc              = "Threads and pthreads support.";
+  thread_db_ops.to_files_info       = thread_db_files_info;
+  thread_db_ops.to_create_inferior  = thread_db_create_inferior;
+  thread_db_ops.to_detach           = thread_db_detach;
+  thread_db_ops.to_wait             = thread_db_wait;
+  thread_db_ops.to_resume           = thread_db_resume;
+  thread_db_ops.to_mourn_inferior   = thread_db_mourn_inferior;
+  thread_db_ops.to_kill             = thread_db_kill;
+  thread_db_ops.to_xfer_memory      = thread_db_xfer_memory;
+  thread_db_ops.to_fetch_registers  = thread_db_fetch_registers;
+  thread_db_ops.to_store_registers  = thread_db_store_registers;
+  thread_db_ops.to_thread_alive     = thread_db_alive;
+  thread_db_ops.to_find_new_threads = thread_db_find_new_threads;
+  thread_db_ops.to_pid_to_str       = thread_db_pid_to_str;
+  thread_db_ops.to_stratum          = thread_stratum;
+  thread_db_ops.to_has_thread_control = tc_schedlock;
+  thread_db_ops.to_magic            = OPS_MAGIC;
+}
+#endif /* HAVE_STDINT_H */
+
+/*
+ * Module constructor / initializer function.
+ * If connection to thread_db dynamic library is successful, 
+ * then initialize this module's target vectors and the 
+ * new_objfile hook.
+ */
+
+
+void
+_initialize_thread_db ()
+{
+#ifdef HAVE_STDINT_H   /* stub out entire module, leave initializer empty */
+  if (init_thread_db_library ())
+    {
+      init_thread_db_ops ();
+      add_target (&thread_db_ops);
+      /*
+       * Hook up to the new_objfile event.
+       * If someone is already there, arrange for him to be called
+       * after we are.
+       */
+      target_new_objfile_chain = target_new_objfile_hook;
+      target_new_objfile_hook  = thread_db_new_objfile;
+    }
+#endif /* HAVE_STDINT_H */
+}
+