* 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.
This commit is contained in:
Daniel Jacobowitz
2004-10-16 17:42:00 +00:00
parent f6de3c42a3
commit fd5008162e
7 changed files with 241 additions and 101 deletions

View File

@ -1,3 +1,25 @@
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> 2004-10-16 Daniel Jacobowitz <dan@debian.org>
* configure.in: Test for -rdynamic. * configure.in: Test for -rdynamic.

View File

@ -1,5 +1,5 @@
/* GNU/Linux/i386 specific low level interface, for the remote server for GDB. /* 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. Free Software Foundation, Inc.
This file is part of GDB. This file is part of GDB.
@ -23,10 +23,30 @@
#include "linux-low.h" #include "linux-low.h"
#include "i387-fp.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 #ifdef HAVE_SYS_REG_H
#include <sys/reg.h> #include <sys/reg.h>
#endif #endif
#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 25
#endif
/* This module only supports access to the general purpose registers. */ /* This module only supports access to the general purpose registers. */
#define i386_num_regs 16 #define i386_num_regs 16
@ -43,6 +63,22 @@ static int i386_regmap[] =
DS * 4, ES * 4, FS * 4, GS * 4 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 static int
i386_cannot_store_register (int regno) i386_cannot_store_register (int regno)
{ {

View File

@ -35,6 +35,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/syscall.h>
/* ``all_threads'' is keyed by the LWP ID - it should be the thread ID instead, /* ``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 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); struct process_info *process = get_thread_process (thread);
int wstat; 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 do
{ {
ptrace (PTRACE_KILL, pid_of (process), 0, 0); ptrace (PTRACE_KILL, pid_of (process), 0, 0);
@ -235,7 +243,21 @@ linux_kill_one_process (struct inferior_list_entry *entry)
static void static void
linux_kill (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); 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 static void
@ -709,6 +731,30 @@ retry:
return ((unsigned char) WSTOPSIG (w)); 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 static void
send_sigstop (struct inferior_list_entry *entry) send_sigstop (struct inferior_list_entry *entry)
{ {
@ -728,7 +774,7 @@ send_sigstop (struct inferior_list_entry *entry)
if (debug_threads) if (debug_threads)
fprintf (stderr, "Sending sigstop to process %d\n", process->head.id); 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; process->sigstop_sent = 1;
} }
@ -1388,10 +1434,10 @@ linux_send_signal (int signum)
struct process_info *process; struct process_info *process;
process = get_thread_process (current_inferior); process = get_thread_process (current_inferior);
kill (process->lwpid, signum); kill_lwp (process->lwpid, signum);
} }
else else
kill (signal_pid, signum); kill_lwp (signal_pid, signum);
} }
/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET /* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET

View File

@ -1,6 +1,6 @@
/* GNU/Linux/x86-64 specific low level interface, for the remote server /* GNU/Linux/x86-64 specific low level interface, for the remote server
for GDB. for GDB.
Copyright 2002 Copyright 2002, 2004
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of GDB. This file is part of GDB.
@ -24,10 +24,29 @@
#include "linux-low.h" #include "linux-low.h"
#include "i387-fp.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/reg.h>
#include <sys/procfs.h> #include <sys/procfs.h>
#include <sys/ptrace.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[] = { static int x86_64_regmap[] = {
RAX * 8, RBX * 8, RCX * 8, RDX * 8, RAX * 8, RBX * 8, RCX * 8, RDX * 8,
RSI * 8, RDI * 8, RBP * 8, RSP * 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)) #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 static void
x86_64_fill_gregset (void *buf) x86_64_fill_gregset (void *buf)
{ {

View File

@ -1,5 +1,5 @@
/* libthread_db helper functions for the remote server for GDB. /* libthread_db helper functions for the remote server for GDB.
Copyright 2002 Copyright 2002, 2004
Free Software Foundation, Inc. Free Software Foundation, Inc.
Contributed by MontaVista Software. Contributed by MontaVista Software.
@ -48,11 +48,11 @@ typedef void *gdb_ps_read_buf_t;
typedef const void *gdb_ps_write_buf_t; typedef const void *gdb_ps_write_buf_t;
typedef size_t gdb_ps_size_t; typedef size_t gdb_ps_size_t;
/* FIXME redo this right */ #ifdef HAVE_LINUX_REGSETS
#if 0 #define HAVE_REGSETS
#ifndef HAVE_LINUX_REGSETS #endif
#error HAVE_LINUX_REGSETS required!
#else #ifdef HAVE_REGSETS
static struct regset_info * static struct regset_info *
gregset_info(void) gregset_info(void)
{ {
@ -67,22 +67,6 @@ gregset_info(void)
return &target_regsets[i]; 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 #endif
/* Search for the symbol named NAME within the object named OBJ within /* 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_err_e
ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset) 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; struct thread_info *reg_inferior, *save_inferior;
void *regcache;
reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, reg_inferior = (struct thread_info *) find_inferior_id (&all_threads,
lwpid); lwpid);
@ -140,16 +123,14 @@ ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset)
save_inferior = current_inferior; save_inferior = current_inferior;
current_inferior = reg_inferior; current_inferior = reg_inferior;
regcache = new_register_cache (); the_target->fetch_registers (0);
the_target->fetch_registers (0, regcache); gregset_info()->fill_function (gregset);
gregset_info()->fill_function (gregset, regcache);
free_register_cache (regcache);
current_inferior = save_inferior; current_inferior = save_inferior;
return PS_OK; return PS_OK;
#endif #else
/* FIXME */
return PS_ERR; return PS_ERR;
#endif
} }
/* Set the general registers of LWP LWPID within the target process PH /* 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_err_e
ps_lsetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prgregset_t gregset) ps_lsetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prgregset_t gregset)
{ {
#if 0 /* Unneeded. */
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 */
return PS_ERR; return PS_ERR;
} }
@ -189,27 +150,7 @@ ps_err_e
ps_lgetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, ps_lgetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid,
gdb_prfpregset_t *fpregset) gdb_prfpregset_t *fpregset)
{ {
#if 0 /* Unneeded. */
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 */
return PS_ERR; return PS_ERR;
} }
@ -220,27 +161,7 @@ ps_err_e
ps_lsetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, ps_lsetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid,
const gdb_prfpregset_t *fpregset) const gdb_prfpregset_t *fpregset)
{ {
#if 0 /* Unneeded. */
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 */
return PS_ERR; return PS_ERR;
} }

View File

@ -37,6 +37,17 @@
#include <unistd.h> #include <unistd.h>
#include <arpa/inet.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; int remote_debug = 0;
struct ui_file *gdb_stdlog; struct ui_file *gdb_stdlog;
@ -353,13 +364,14 @@ input_interrupt (int unused)
if (select (remote_desc + 1, &readset, 0, 0, &immediate) > 0) if (select (remote_desc + 1, &readset, 0, 0, &immediate) > 0)
{ {
int cc; int cc;
char c; char c = 0;
cc = read (remote_desc, &c, 1); cc = read (remote_desc, &c, 1);
if (cc != 1 || c != '\003') 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; return;
} }
@ -385,16 +397,33 @@ unblock_async_io (void)
sigprocmask (SIG_UNBLOCK, &sigio_set, NULL); 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 void
enable_async_io (void) enable_async_io (void)
{ {
if (async_io_enabled)
return;
signal (SIGIO, input_interrupt); signal (SIGIO, input_interrupt);
async_io_enabled = 1;
} }
/* Disable asynchronous I/O. */
void void
disable_async_io (void) disable_async_io (void)
{ {
if (!async_io_enabled)
return;
signal (SIGIO, SIG_IGN); signal (SIGIO, SIG_IGN);
async_io_enabled = 0;
} }
/* Returns next char from remote GDB. -1 if error. */ /* 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); 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 int
look_up_one_symbol (const char *name, CORE_ADDR *addrp) look_up_one_symbol (const char *name, CORE_ADDR *addrp)
{ {
char own_buf[266], *p, *q; char own_buf[266], *p, *q;
int len; 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. */ /* Send the request. */
strcpy (own_buf, "qSymbol:"); strcpy (own_buf, "qSymbol:");
@ -731,6 +772,13 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp)
return 0; return 0;
decode_address (addrp, p, q - p); 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; return 1;
} }

View File

@ -312,11 +312,36 @@ thread_db_find_new_threads (void)
error ("Cannot find new threads: %s", thread_db_err_str (err)); 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 int
thread_db_init () thread_db_init ()
{ {
int err; 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; proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id;
err = td_ta_new (&proc_handle, &thread_agent); err = td_ta_new (&proc_handle, &thread_agent);
@ -332,6 +357,7 @@ thread_db_init ()
if (thread_db_enable_reporting () == 0) if (thread_db_enable_reporting () == 0)
return 0; return 0;
thread_db_find_new_threads (); thread_db_find_new_threads ();
thread_db_look_up_symbols ();
return 1; return 1;
default: default: