diff options
-rw-r--r-- | gdb/gdbserver/ChangeLog | 22 | ||||
-rw-r--r-- | gdb/gdbserver/linux-i386-low.c | 38 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 52 | ||||
-rw-r--r-- | gdb/gdbserver/linux-x86-64-low.c | 43 | ||||
-rw-r--r-- | gdb/gdbserver/proc-service.c | 107 | ||||
-rw-r--r-- | gdb/gdbserver/remote-utils.c | 54 | ||||
-rw-r--r-- | gdb/gdbserver/thread-db.c | 26 |
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: |