* 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>
* configure.in: Test for -rdynamic.

View file

@ -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)
{

View file

@ -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

View file

@ -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)
{

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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: