/* Low level interface to ptrace, for the remote server for GDB.
- Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This file is part of GDB.
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. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "server.h"
#include "linux-low.h"
#include <string.h>
#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
static int use_regsets_p = 1;
#endif
-extern int errno;
-
int debug_threads = 0;
#define pid_of(proc) ((proc)->head.id)
}
static void *
-add_process (int pid)
+add_process (unsigned long pid)
{
struct process_info *process;
}
new_process = add_process (pid);
- add_thread (pid, new_process);
+ add_thread (pid, new_process, pid);
return pid;
}
/* Attach to an inferior process. */
void
-linux_attach_lwp (int pid, int tid)
+linux_attach_lwp (unsigned long pid, unsigned long tid)
{
struct process_info *new_process;
if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0)
{
- fprintf (stderr, "Cannot attach to process %d: %s (%d)\n", pid,
+ fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
strerror (errno), errno);
fflush (stderr);
}
new_process = (struct process_info *) add_process (pid);
- add_thread (tid, new_process);
+ add_thread (tid, new_process, pid);
/* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
brings it to a halt. We should ignore that SIGSTOP and resume the process
}
int
-linux_attach (int pid)
+linux_attach (unsigned long pid)
{
struct process_info *process;
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);
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
/* Return nonzero if the given thread is still alive. */
static int
-linux_thread_alive (int tid)
+linux_thread_alive (unsigned long tid)
{
if (find_inferior_id (&all_threads, tid) != NULL)
return 1;
event_child = (struct process_info *)
find_inferior (&all_processes, status_pending_p, NULL);
if (debug_threads && event_child)
- fprintf (stderr, "Got a pending child %d\n", event_child->lwpid);
+ fprintf (stderr, "Got a pending child %ld\n", event_child->lwpid);
}
else
{
if (event_child->status_pending_p)
{
if (debug_threads)
- fprintf (stderr, "Got an event from pending child %d (%04x)\n",
+ fprintf (stderr, "Got an event from pending child %ld (%04x)\n",
event_child->lwpid, event_child->status_pending);
wstat = event_child->status_pending;
event_child->status_pending_p = 0;
if (! WIFSTOPPED (wstat))
{
if (debug_threads)
- fprintf (stderr, "Thread %d (LWP %d) exiting\n",
+ fprintf (stderr, "Thread %ld (LWP %ld) exiting\n",
event_child->tid, event_child->head.id);
/* If the last thread is exiting, just return. */
|| WSTOPSIG (wstat) == __SIGRTMIN + 1))
{
if (debug_threads)
- fprintf (stderr, "Ignored signal %d for %d (LWP %d).\n",
+ fprintf (stderr, "Ignored signal %d for %ld (LWP %ld).\n",
WSTOPSIG (wstat), event_child->tid,
event_child->head.id);
linux_resume_one_process (&event_child->head,
/* If we were single-stepping, we definitely want to report the
SIGTRAP. The single-step operation has completed, so also
- clear the stepping flag; in general this does not matter,
+ clear the stepping flag; in general this does not matter,
because the SIGTRAP will be reported to the client, which
will give us a new action for this thread, but clear it for
consistency anyway. It's safe to clear the stepping flag
then we need to make sure we restart the other threads. We could
pick a thread at random or restart all; restarting all is less
arbitrary. */
- if (cont_thread > 0)
+ if (cont_thread != 0 && cont_thread != -1)
{
child = (struct thread_info *) find_inferior_id (&all_threads,
cont_thread);
}
enable_async_io ();
+ unblock_async_io ();
w = linux_wait_for_event (child);
stop_all_processes ();
disable_async_io ();
fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w));
*status = 'W';
clear_inferiors ();
+ free (all_processes.head);
+ all_processes.head = all_processes.tail = NULL;
return ((unsigned char) WEXITSTATUS (w));
}
else if (!WIFSTOPPED (w))
{
fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w));
- clear_inferiors ();
*status = 'X';
+ clear_inferiors ();
+ free (all_processes.head);
+ all_processes.head = all_processes.tail = NULL;
return ((unsigned char) WTERMSIG (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 (unsigned long 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)
{
}
if (debug_threads)
- fprintf (stderr, "Sending sigstop to process %d\n", process->head.id);
+ fprintf (stderr, "Sending sigstop to process %ld\n", process->head.id);
- kill (process->head.id, SIGSTOP);
+ kill_lwp (process->head.id, SIGSTOP);
process->sigstop_sent = 1;
}
{
struct process_info *process = (struct process_info *) entry;
struct thread_info *saved_inferior, *thread;
- int wstat, saved_tid;
+ int wstat;
+ unsigned long saved_tid;
if (process->stopped)
return;
current_inferior = get_process_thread (process);
if (debug_threads)
- fprintf (stderr, "Resuming process %d (%s, signal %d, stop %s)\n", inferior_pid,
+ fprintf (stderr, "Resuming process %ld (%s, signal %d, stop %s)\n", inferior_pid,
step ? "step" : "continue", signal,
process->stop_expected ? "expected" : "not expected");
check_removed_breakpoint (process);
- if (debug_threads && the_low_target.get_pc != NULL)
+ if (debug_threads && the_low_target.get_pc != NULL)
{
fprintf (stderr, " ");
(long) (*the_low_target.get_pc) ();
if (pending_flag)
for_each_inferior (&all_threads, linux_queue_one_thread);
else
- for_each_inferior (&all_threads, linux_continue_one_thread);
+ {
+ block_async_io ();
+ enable_async_io ();
+ for_each_inferior (&all_threads, linux_continue_one_thread);
+ }
}
#ifdef HAVE_LINUX_USRREGS
fetch_register (int regno)
{
CORE_ADDR regaddr;
- register int i;
+ int i, size;
char *buf;
if (regno >= the_low_target.num_regs)
regaddr = register_addr (regno);
if (regaddr == -1)
return;
- buf = alloca (register_size (regno));
- for (i = 0; i < register_size (regno); i += sizeof (PTRACE_XFER_TYPE))
+ size = (register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ & - sizeof (PTRACE_XFER_TYPE);
+ buf = alloca (size);
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
{
errno = 0;
*(PTRACE_XFER_TYPE *) (buf + i) =
goto error_exit;
}
}
- supply_register (regno, buf);
+ if (the_low_target.left_pad_xfer
+ && register_size (regno) < sizeof (PTRACE_XFER_TYPE))
+ supply_register (regno, (buf + sizeof (PTRACE_XFER_TYPE)
+ - register_size (regno)));
+ else
+ supply_register (regno, buf);
error_exit:;
}
usr_store_inferior_registers (int regno)
{
CORE_ADDR regaddr;
- int i;
+ int i, size;
char *buf;
if (regno >= 0)
if (regaddr == -1)
return;
errno = 0;
- buf = alloca (register_size (regno));
- collect_register (regno, buf);
- for (i = 0; i < register_size (regno); i += sizeof (PTRACE_XFER_TYPE))
+ size = (register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ & - sizeof (PTRACE_XFER_TYPE);
+ buf = alloca (size);
+ memset (buf, 0, size);
+ if (the_low_target.left_pad_xfer
+ && register_size (regno) < sizeof (PTRACE_XFER_TYPE))
+ collect_register (regno, (buf + sizeof (PTRACE_XFER_TYPE)
+ - register_size (regno)));
+ else
+ collect_register (regno, buf);
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
{
errno = 0;
ptrace (PTRACE_POKEUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr,
regsets_fetch_inferior_registers ()
{
struct regset_info *regset;
+ int saw_general_regs = 0;
regset = target_regsets;
else
{
char s[256];
- sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%d",
+ sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%ld",
inferior_pid);
perror (s);
}
}
+ else if (regset->type == GENERAL_REGS)
+ saw_general_regs = 1;
regset->store_function (buf);
regset ++;
}
- return 0;
+ if (saw_general_regs)
+ return 0;
+ else
+ return 1;
}
static int
regsets_store_inferior_registers ()
{
struct regset_info *regset;
+ int saw_general_regs = 0;
regset = target_regsets;
perror ("Warning: ptrace(regsets_store_inferior_registers)");
}
}
+ else if (regset->type == GENERAL_REGS)
+ saw_general_regs = 1;
regset ++;
free (buf);
}
+ if (saw_general_regs)
+ return 0;
+ else
+ return 1;
return 0;
}
/* Copy LEN bytes from inferior's memory starting at MEMADDR
to debugger memory starting at MYADDR. */
-static void
-linux_read_memory (CORE_ADDR memaddr, char *myaddr, int len)
+static int
+linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
register int i;
/* Round starting address down to longword boundary. */
register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
/* Round ending address up; get number of longwords that makes. */
- register int count
- = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
+ register int count
+ = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
/ sizeof (PTRACE_XFER_TYPE);
/* Allocate buffer of that many longwords. */
- register PTRACE_XFER_TYPE *buffer
+ register PTRACE_XFER_TYPE *buffer
= (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
/* Read all the longwords */
for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
{
+ errno = 0;
buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
+ if (errno)
+ return errno;
}
/* Copy appropriate bytes out of the buffer. */
memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), len);
+
+ return 0;
}
/* Copy LEN bytes of data from debugger memory at MYADDR
returns the value of errno. */
static int
-linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len)
+linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
{
register int i;
/* Round starting address down to longword boundary. */
static void
linux_send_signal (int signum)
{
- extern int signal_pid;
+ extern unsigned long signal_pid;
- if (cont_thread > 0)
+ if (cont_thread != 0 && cont_thread != -1)
{
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
+ to debugger memory starting at MYADDR. */
+
+static int
+linux_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
+{
+ char filename[PATH_MAX];
+ int fd, n;
+
+ snprintf (filename, sizeof filename, "/proc/%ld/auxv", inferior_pid);
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (offset != (CORE_ADDR) 0
+ && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+ n = -1;
+ else
+ n = read (fd, myaddr, len);
+
+ close (fd);
+
+ return n;
+}
+
+/* These watchpoint related wrapper functions simply pass on the function call
+ if the target has registered a corresponding function. */
+
+static int
+linux_insert_watchpoint (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.insert_watchpoint != NULL)
+ return the_low_target.insert_watchpoint (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+linux_remove_watchpoint (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.remove_watchpoint != NULL)
+ return the_low_target.remove_watchpoint (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+linux_stopped_by_watchpoint (void)
+{
+ if (the_low_target.stopped_by_watchpoint != NULL)
+ return the_low_target.stopped_by_watchpoint ();
+ else
+ return 0;
+}
+
+static CORE_ADDR
+linux_stopped_data_address (void)
+{
+ if (the_low_target.stopped_data_address != NULL)
+ return the_low_target.stopped_data_address ();
+ else
+ return 0;
}
-\f
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
linux_write_memory,
linux_look_up_symbols,
linux_send_signal,
+ linux_read_auxv,
+ linux_insert_watchpoint,
+ linux_remove_watchpoint,
+ linux_stopped_by_watchpoint,
+ linux_stopped_data_address,
};
static void