summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Molenda <jsm@bugshack.cygnus.com>1999-12-22 21:45:13 +0000
committerJason Molenda <jsm@bugshack.cygnus.com>1999-12-22 21:45:13 +0000
commit581a81d2bed63e5910a0715d6bf685cfc4746bc6 (patch)
treeaa07467628a12e68a4537d1cae0aa499b8918317
parentcb92cd43207fff501df88e87d0d96b38a24a86d5 (diff)
downloadgdb-581a81d2bed63e5910a0715d6bf685cfc4746bc6.tar.gz
Initial revision
-rw-r--r--gdb/arm-linux-nat.c547
-rw-r--r--gdb/config/arm/embed.mt7
-rw-r--r--gdb/config/arm/linux.mh8
-rw-r--r--gdb/config/arm/linux.mt5
-rw-r--r--gdb/config/arm/nm-linux.h46
-rw-r--r--gdb/config/arm/tm-embed.h66
-rw-r--r--gdb/config/arm/tm-linux.h122
-rw-r--r--gdb/config/arm/xm-linux.h37
-rw-r--r--gdb/gdb_proc_service.h24
-rw-r--r--gdb/gdb_thread_db.h427
-rw-r--r--gdb/lin-thread.c2140
11 files changed, 3429 insertions, 0 deletions
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
new file mode 100644
index 00000000000..f7380503043
--- /dev/null
+++ b/gdb/arm-linux-nat.c
@@ -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
index 00000000000..c854d17a71a
--- /dev/null
+++ b/gdb/config/arm/embed.mt
@@ -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
index 00000000000..3c6dccc82d2
--- /dev/null
+++ b/gdb/config/arm/linux.mh
@@ -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
index 00000000000..b85ae48bf6d
--- /dev/null
+++ b/gdb/config/arm/linux.mt
@@ -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
index 00000000000..125d72f67ff
--- /dev/null
+++ b/gdb/config/arm/nm-linux.h
@@ -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
index 00000000000..f42b4f2ead8
--- /dev/null
+++ b/gdb/config/arm/tm-embed.h
@@ -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
index 00000000000..27cc0d1573b
--- /dev/null
+++ b/gdb/config/arm/tm-linux.h
@@ -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
index 00000000000..ef724b5d9bb
--- /dev/null
+++ b/gdb/config/arm/xm-linux.h
@@ -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
index 00000000000..dfbf9647d0e
--- /dev/null
+++ b/gdb/gdb_proc_service.h
@@ -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
index 00000000000..5bb196344b1
--- /dev/null
+++ b/gdb/gdb_thread_db.h
@@ -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
index 00000000000..2f255c0e54a
--- /dev/null
+++ b/gdb/lin-thread.c
@@ -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 */
+}
+