summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/gdbserver/ChangeLog22
-rw-r--r--gdb/gdbserver/linux-i386-low.c38
-rw-r--r--gdb/gdbserver/linux-low.c52
-rw-r--r--gdb/gdbserver/linux-x86-64-low.c43
-rw-r--r--gdb/gdbserver/proc-service.c107
-rw-r--r--gdb/gdbserver/remote-utils.c54
-rw-r--r--gdb/gdbserver/thread-db.c26
7 files changed, 241 insertions, 101 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index ea3b0829d88..96df7e507ee 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,5 +1,27 @@
2004-10-16 Daniel Jacobowitz <dan@debian.org>
+ * linux-i386-low.c (ps_get_thread_area): New.
+ * linux-x86-64-low.c (ps_get_thread_area): New.
+ * linux-low.c: Include <sys/syscall.h>.
+ (linux_kill_one_process): Don't kill the first thread here.
+ (linux_kill): Kill the first thread here.
+ (kill_lwp): New function.
+ (send_sigstop, linux_send_signal): Use it.
+ * proc-service.c: Clean up #ifdefs.
+ (fpregset_info): Delete.
+ (ps_lgetregs): Update and enable implementation.
+ (ps_lsetregs, ps_lgetfpregs, ps_lsetfpregs): Remove disabled
+ implementations.
+ * remote-utils.c (struct sym_cache, symbol_cache): New.
+ (input_interrupt): Print a clearer message.
+ (async_io_enabled): New variable.
+ (enable_async_io, disable_async_io): Use it. Update comments.
+ (look_up_one_symbol): Use the symbol cache.
+ * thread-db.c (thread_db_look_up_symbols): New function.
+ (thread_db_init): Update comments. Call thread_db_look_up_symbols.
+
+2004-10-16 Daniel Jacobowitz <dan@debian.org>
+
* configure.in: Test for -rdynamic.
* configure: Regenerated.
* Makefile (INTERNAL_LDFLAGS): New.
diff --git a/gdb/gdbserver/linux-i386-low.c b/gdb/gdbserver/linux-i386-low.c
index b79b601ae8f..d8cc4ecaed9 100644
--- a/gdb/gdbserver/linux-i386-low.c
+++ b/gdb/gdbserver/linux-i386-low.c
@@ -1,5 +1,5 @@
/* GNU/Linux/i386 specific low level interface, for the remote server for GDB.
- Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002
+ Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2004
Free Software Foundation, Inc.
This file is part of GDB.
@@ -23,10 +23,30 @@
#include "linux-low.h"
#include "i387-fp.h"
+/* Correct for all GNU/Linux targets (for quite some time). */
+#define GDB_GREGSET_T elf_gregset_t
+#define GDB_FPREGSET_T elf_fpregset_t
+
+#ifndef HAVE_ELF_FPREGSET_T
+/* Make sure we have said types. Not all platforms bring in <linux/elf.h>
+ via <sys/procfs.h>. */
+#ifdef HAVE_LINUX_ELF_H
+#include <linux/elf.h>
+#endif
+#endif
+
+#include "../gdb_proc_service.h"
+
+#include <sys/ptrace.h>
+
#ifdef HAVE_SYS_REG_H
#include <sys/reg.h>
#endif
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
/* This module only supports access to the general purpose registers. */
#define i386_num_regs 16
@@ -43,6 +63,22 @@ static int i386_regmap[] =
DS * 4, ES * 4, FS * 4, GS * 4
};
+/* Called by libthread_db. */
+
+ps_err_e
+ps_get_thread_area (const struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ unsigned int desc[4];
+
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid,
+ (void *) idx, (unsigned long) &desc) < 0)
+ return PS_ERR;
+
+ *(int *)base = desc[1];
+ return PS_OK;
+}
+
static int
i386_cannot_store_register (int regno)
{
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 5733180e997..9c41fbb2281 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/syscall.h>
/* ``all_threads'' is keyed by the LWP ID - it should be the thread ID instead,
however. This requires changing the ID in place when we go from !using_threads
@@ -223,6 +224,13 @@ linux_kill_one_process (struct inferior_list_entry *entry)
struct process_info *process = get_thread_process (thread);
int wstat;
+ /* We avoid killing the first thread here, because of a Linux kernel (at
+ least 2.6.0-test7 through 2.6.8-rc4) bug; if we kill the parent before
+ the children get a chance to be reaped, it will remain a zombie
+ forever. */
+ if (entry == all_threads.head)
+ return;
+
do
{
ptrace (PTRACE_KILL, pid_of (process), 0, 0);
@@ -235,7 +243,21 @@ linux_kill_one_process (struct inferior_list_entry *entry)
static void
linux_kill (void)
{
+ struct thread_info *thread = (struct thread_info *) all_threads.head;
+ struct process_info *process = get_thread_process (thread);
+ int wstat;
+
for_each_inferior (&all_threads, linux_kill_one_process);
+
+ /* See the comment in linux_kill_one_process. We did not kill the first
+ thread in the list, so do so now. */
+ do
+ {
+ ptrace (PTRACE_KILL, pid_of (process), 0, 0);
+
+ /* Make sure it died. The loop is most likely unnecessary. */
+ wstat = linux_wait_for_event (thread);
+ } while (WIFSTOPPED (wstat));
}
static void
@@ -709,6 +731,30 @@ retry:
return ((unsigned char) WSTOPSIG (w));
}
+/* Send a signal to an LWP. For LinuxThreads, kill is enough; however, if
+ thread groups are in use, we need to use tkill. */
+
+static int
+kill_lwp (int lwpid, int signo)
+{
+ static int tkill_failed;
+
+ errno = 0;
+
+#ifdef SYS_tkill
+ if (!tkill_failed)
+ {
+ int ret = syscall (SYS_tkill, lwpid, signo);
+ if (errno != ENOSYS)
+ return ret;
+ errno = 0;
+ tkill_failed = 1;
+ }
+#endif
+
+ return kill (lwpid, signo);
+}
+
static void
send_sigstop (struct inferior_list_entry *entry)
{
@@ -728,7 +774,7 @@ send_sigstop (struct inferior_list_entry *entry)
if (debug_threads)
fprintf (stderr, "Sending sigstop to process %d\n", process->head.id);
- kill (process->head.id, SIGSTOP);
+ kill_lwp (process->head.id, SIGSTOP);
process->sigstop_sent = 1;
}
@@ -1388,10 +1434,10 @@ linux_send_signal (int signum)
struct process_info *process;
process = get_thread_process (current_inferior);
- kill (process->lwpid, signum);
+ kill_lwp (process->lwpid, signum);
}
else
- kill (signal_pid, signum);
+ kill_lwp (signal_pid, signum);
}
/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
diff --git a/gdb/gdbserver/linux-x86-64-low.c b/gdb/gdbserver/linux-x86-64-low.c
index 4ee1ae48190..45f00b0c880 100644
--- a/gdb/gdbserver/linux-x86-64-low.c
+++ b/gdb/gdbserver/linux-x86-64-low.c
@@ -1,6 +1,6 @@
/* GNU/Linux/x86-64 specific low level interface, for the remote server
for GDB.
- Copyright 2002
+ Copyright 2002, 2004
Free Software Foundation, Inc.
This file is part of GDB.
@@ -24,10 +24,29 @@
#include "linux-low.h"
#include "i387-fp.h"
+/* Correct for all GNU/Linux targets (for quite some time). */
+#define GDB_GREGSET_T elf_gregset_t
+#define GDB_FPREGSET_T elf_fpregset_t
+
+#ifndef HAVE_ELF_FPREGSET_T
+/* Make sure we have said types. Not all platforms bring in <linux/elf.h>
+ via <sys/procfs.h>. */
+#ifdef HAVE_LINUX_ELF_H
+#include <linux/elf.h>
+#endif
+#endif
+
+#include "../gdb_proc_service.h"
+
#include <sys/reg.h>
#include <sys/procfs.h>
#include <sys/ptrace.h>
+/* This definition comes from prctl.h, but some kernels may not have it. */
+#ifndef PTRACE_ARCH_PRCTL
+#define PTRACE_ARCH_PRCTL 30
+#endif
+
static int x86_64_regmap[] = {
RAX * 8, RBX * 8, RCX * 8, RDX * 8,
RSI * 8, RDI * 8, RBP * 8, RSP * 8,
@@ -39,6 +58,28 @@ static int x86_64_regmap[] = {
#define X86_64_NUM_GREGS (sizeof(x86_64_regmap)/sizeof(int))
+/* Called by libthread_db. */
+
+ps_err_e
+ps_get_thread_area (const struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ switch (idx)
+ {
+ case FS:
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0)
+ return PS_OK;
+ break;
+ case GS:
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
+ return PS_OK;
+ break;
+ default:
+ return PS_BADADDR;
+ }
+ return PS_ERR;
+}
+
static void
x86_64_fill_gregset (void *buf)
{
diff --git a/gdb/gdbserver/proc-service.c b/gdb/gdbserver/proc-service.c
index becf565529b..5b83dec7465 100644
--- a/gdb/gdbserver/proc-service.c
+++ b/gdb/gdbserver/proc-service.c
@@ -1,5 +1,5 @@
/* libthread_db helper functions for the remote server for GDB.
- Copyright 2002
+ Copyright 2002, 2004
Free Software Foundation, Inc.
Contributed by MontaVista Software.
@@ -48,11 +48,11 @@ typedef void *gdb_ps_read_buf_t;
typedef const void *gdb_ps_write_buf_t;
typedef size_t gdb_ps_size_t;
-/* FIXME redo this right */
-#if 0
-#ifndef HAVE_LINUX_REGSETS
-#error HAVE_LINUX_REGSETS required!
-#else
+#ifdef HAVE_LINUX_REGSETS
+#define HAVE_REGSETS
+#endif
+
+#ifdef HAVE_REGSETS
static struct regset_info *
gregset_info(void)
{
@@ -67,22 +67,6 @@ gregset_info(void)
return &target_regsets[i];
}
-
-static struct regset_info *
-fpregset_info(void)
-{
- int i = 0;
-
- while (target_regsets[i].size != -1)
- {
- if (target_regsets[i].type == FP_REGS)
- break;
- i++;
- }
-
- return &target_regsets[i];
-}
-#endif
#endif
/* Search for the symbol named NAME within the object named OBJ within
@@ -128,9 +112,8 @@ ps_pdwrite (gdb_ps_prochandle_t ph, paddr_t addr,
ps_err_e
ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset)
{
-#if 0
+#ifdef HAVE_REGSETS
struct thread_info *reg_inferior, *save_inferior;
- void *regcache;
reg_inferior = (struct thread_info *) find_inferior_id (&all_threads,
lwpid);
@@ -140,16 +123,14 @@ ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset)
save_inferior = current_inferior;
current_inferior = reg_inferior;
- regcache = new_register_cache ();
- the_target->fetch_registers (0, regcache);
- gregset_info()->fill_function (gregset, regcache);
- free_register_cache (regcache);
+ the_target->fetch_registers (0);
+ gregset_info()->fill_function (gregset);
current_inferior = save_inferior;
return PS_OK;
-#endif
- /* FIXME */
+#else
return PS_ERR;
+#endif
}
/* Set the general registers of LWP LWPID within the target process PH
@@ -158,27 +139,7 @@ ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset)
ps_err_e
ps_lsetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prgregset_t gregset)
{
-#if 0
- struct thread_info *reg_inferior, *save_inferior;
- void *regcache;
-
- reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid);
- if (reg_inferior == NULL)
- return PS_ERR;
-
- save_inferior = current_inferior;
- current_inferior = reg_inferior;
-
- regcache = new_register_cache ();
- gregset_info()->store_function (gregset, regcache);
- the_target->store_registers (0, regcache);
- free_register_cache (regcache);
-
- current_inferior = save_inferior;
-
- return PS_OK;
-#endif
- /* FIXME */
+ /* Unneeded. */
return PS_ERR;
}
@@ -189,27 +150,7 @@ ps_err_e
ps_lgetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid,
gdb_prfpregset_t *fpregset)
{
-#if 0
- struct thread_info *reg_inferior, *save_inferior;
- void *regcache;
-
- reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid);
- if (reg_inferior == NULL)
- return PS_ERR;
-
- save_inferior = current_inferior;
- current_inferior = reg_inferior;
-
- regcache = new_register_cache ();
- the_target->fetch_registers (0, regcache);
- fpregset_info()->fill_function (fpregset, regcache);
- free_register_cache (regcache);
-
- current_inferior = save_inferior;
-
- return PS_OK;
-#endif
- /* FIXME */
+ /* Unneeded. */
return PS_ERR;
}
@@ -220,27 +161,7 @@ ps_err_e
ps_lsetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid,
const gdb_prfpregset_t *fpregset)
{
-#if 0
- struct thread_info *reg_inferior, *save_inferior;
- void *regcache;
-
- reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid);
- if (reg_inferior == NULL)
- return PS_ERR;
-
- save_inferior = current_inferior;
- current_inferior = reg_inferior;
-
- regcache = new_register_cache ();
- fpregset_info()->store_function (fpregset, regcache);
- the_target->store_registers (0, regcache);
- free_register_cache (regcache);
-
- current_inferior = save_inferior;
-
- return PS_OK;
-#endif
- /* FIXME */
+ /* Unneeded. */
return PS_ERR;
}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 26b267a3aaf..0d2bdef06a0 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -37,6 +37,17 @@
#include <unistd.h>
#include <arpa/inet.h>
+/* A cache entry for a successfully looked-up symbol. */
+struct sym_cache
+{
+ const char *name;
+ CORE_ADDR addr;
+ struct sym_cache *next;
+};
+
+/* The symbol cache. */
+static struct sym_cache *symbol_cache;
+
int remote_debug = 0;
struct ui_file *gdb_stdlog;
@@ -353,13 +364,14 @@ input_interrupt (int unused)
if (select (remote_desc + 1, &readset, 0, 0, &immediate) > 0)
{
int cc;
- char c;
+ char c = 0;
cc = read (remote_desc, &c, 1);
if (cc != 1 || c != '\003')
{
- fprintf (stderr, "input_interrupt, cc = %d c = %d\n", cc, c);
+ fprintf (stderr, "input_interrupt, count = %d c = %d ('%c')\n",
+ cc, c, c);
return;
}
@@ -385,16 +397,33 @@ unblock_async_io (void)
sigprocmask (SIG_UNBLOCK, &sigio_set, NULL);
}
+/* Asynchronous I/O support. SIGIO must be enabled when waiting, in order to
+ accept Control-C from the client, and must be disabled when talking to
+ the client. */
+
+/* Current state of asynchronous I/O. */
+static int async_io_enabled;
+
+/* Enable asynchronous I/O. */
void
enable_async_io (void)
{
+ if (async_io_enabled)
+ return;
+
signal (SIGIO, input_interrupt);
+ async_io_enabled = 1;
}
+/* Disable asynchronous I/O. */
void
disable_async_io (void)
{
+ if (!async_io_enabled)
+ return;
+
signal (SIGIO, SIG_IGN);
+ async_io_enabled = 0;
}
/* Returns next char from remote GDB. -1 if error. */
@@ -692,11 +721,23 @@ decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
convert_ascii_to_int (&from[i++], to, *len_ptr);
}
+/* Ask GDB for the address of NAME, and return it in ADDRP if found.
+ Returns 1 if the symbol is found, 0 if it is not, -1 on error. */
+
int
look_up_one_symbol (const char *name, CORE_ADDR *addrp)
{
char own_buf[266], *p, *q;
int len;
+ struct sym_cache *sym;
+
+ /* Check the cache first. */
+ for (sym = symbol_cache; sym; sym = sym->next)
+ if (strcmp (name, sym->name) == 0)
+ {
+ *addrp = sym->addr;
+ return 1;
+ }
/* Send the request. */
strcpy (own_buf, "qSymbol:");
@@ -731,6 +772,13 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp)
return 0;
decode_address (addrp, p, q - p);
+
+ /* Save the symbol in our cache. */
+ sym = malloc (sizeof (*sym));
+ sym->name = strdup (name);
+ sym->addr = *addrp;
+ sym->next = symbol_cache;
+ symbol_cache = sym;
+
return 1;
}
-
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index f3d57a54d62..c6e0c2de3a7 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -312,11 +312,36 @@ thread_db_find_new_threads (void)
error ("Cannot find new threads: %s", thread_db_err_str (err));
}
+/* Cache all future symbols that thread_db might request. We can not
+ request symbols at arbitrary states in the remote protocol, only
+ when the client tells us that new symbols are available. So when
+ we load the thread library, make sure to check the entire list. */
+
+static void
+thread_db_look_up_symbols (void)
+{
+ const char **sym_list = td_symbol_list ();
+ CORE_ADDR unused;
+
+ for (sym_list = td_symbol_list (); *sym_list; sym_list++)
+ look_up_one_symbol (*sym_list, &unused);
+}
+
int
thread_db_init ()
{
int err;
+ /* FIXME drow/2004-10-16: This is the "overall process ID", which
+ GNU/Linux calls tgid, "thread group ID". When we support
+ attaching to threads, the original thread may not be the correct
+ thread. We would have to get the process ID from /proc for NPTL.
+ For LinuxThreads we could do something similar: follow the chain
+ of parent processes until we find the highest one we're attached
+ to, and use its tgid.
+
+ This isn't the only place in gdbserver that assumes that the first
+ process in the list is the thread group leader. */
proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id;
err = td_ta_new (&proc_handle, &thread_agent);
@@ -332,6 +357,7 @@ thread_db_init ()
if (thread_db_enable_reporting () == 0)
return 0;
thread_db_find_new_threads ();
+ thread_db_look_up_symbols ();
return 1;
default: