/* GNU/Linux native-dependent code common to multiple platforms.
- Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
+ 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 "defs.h"
#include "inferior.h"
#endif
#include <sys/ptrace.h>
#include "linux-nat.h"
+#include "linux-fork.h"
#include "gdbthread.h"
#include "gdbcmd.h"
#include "regcache.h"
+#include "inf-ptrace.h"
+#include "auxv.h"
#include <sys/param.h> /* for MAXPATHLEN */
#include <sys/procfs.h> /* for elf_gregset etc. */
#include "elf-bfd.h" /* for elfcore_write_* */
#define __WALL 0x40000000 /* Wait for any child. */
#endif
+/* The single-threaded native GNU/Linux target_ops. We save a pointer for
+ the use of the multi-threaded target. */
+static struct target_ops *linux_ops;
+
+/* The saved to_xfer_partial method, inherited from inf-ptrace.c.
+ Called by our to_xfer_partial. */
+static LONGEST (*super_xfer_partial) (struct target_ops *,
+ enum target_object,
+ const char *, gdb_byte *,
+ const gdb_byte *,
+ ULONGEST, LONGEST);
+
+/* The saved to_mourn_inferior method, inherited from inf-ptrace.c.
+ Called by our to_mourn_inferior. */
+static void (*super_mourn_inferior) (void);
+
static int debug_linux_nat;
+static void
+show_debug_linux_nat (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Debugging of GNU/Linux lwp module is %s.\n"),
+ value);
+}
static int linux_parent_pid;
void
linux_enable_event_reporting (ptid_t ptid)
{
- int pid = ptid_get_pid (ptid);
+ int pid = ptid_get_lwp (ptid);
int options;
+ if (pid == 0)
+ pid = ptid_get_pid (ptid);
+
if (! linux_supports_tracefork (pid))
return;
linux_enable_event_reporting (pid_to_ptid (pid));
}
-void
+static void
linux_child_post_startup_inferior (ptid_t ptid)
{
linux_enable_event_reporting (ptid);
}
-#ifndef LINUX_CHILD_POST_STARTUP_INFERIOR
-void
-child_post_startup_inferior (ptid_t ptid)
-{
- linux_child_post_startup_inferior (ptid);
-}
-#endif
-
int
-child_follow_fork (int follow_child)
+child_follow_fork (struct target_ops *ops, int follow_child)
{
ptid_t last_ptid;
struct target_waitstatus last_status;
get_last_target_status (&last_ptid, &last_status);
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
- parent_pid = ptid_get_pid (last_ptid);
+ parent_pid = ptid_get_lwp (last_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (last_ptid);
child_pid = last_status.value.related_pid;
if (! follow_child)
also, but they'll be reinserted below. */
detach_breakpoints (child_pid);
- fprintf_filtered (gdb_stdout,
- "Detaching after fork from child process %d.\n",
- child_pid);
+ /* Detach new forked process? */
+ if (detach_fork)
+ {
+ if (debug_linux_nat)
+ {
+ target_terminal_ours ();
+ fprintf_filtered (gdb_stdlog,
+ "Detaching after fork from child process %d.\n",
+ child_pid);
+ }
- ptrace (PTRACE_DETACH, child_pid, 0, 0);
+ ptrace (PTRACE_DETACH, child_pid, 0, 0);
+ }
+ else
+ {
+ struct fork_info *fp;
+ /* Retain child fork in ptrace (stopped) state. */
+ fp = find_fork_pid (child_pid);
+ if (!fp)
+ fp = add_fork (child_pid);
+ fork_save_infrun_state (fp, 0);
+ }
if (has_vforked)
{
int status;
ptrace (PTRACE_CONT, parent_pid, 0, 0);
- waitpid (parent_pid, &status, __WALL);
+ my_waitpid (parent_pid, &status, __WALL);
if ((status >> 16) != PTRACE_EVENT_VFORK_DONE)
warning (_("Unexpected waitpid result %06x when waiting for "
"vfork-done"), status);
/* Before detaching from the parent, remove all breakpoints from it. */
remove_breakpoints ();
- fprintf_filtered (gdb_stdout,
- "Attaching after fork to child process %d.\n",
- child_pid);
+ if (debug_linux_nat)
+ {
+ target_terminal_ours ();
+ fprintf_filtered (gdb_stdlog,
+ "Attaching after fork to child process %d.\n",
+ child_pid);
+ }
/* If we're vforking, we may want to hold on to the parent until
the child exits or execs. At exec time we can remove the old
if (has_vforked)
linux_parent_pid = parent_pid;
+ else if (!detach_fork)
+ {
+ struct fork_info *fp;
+ /* Retain parent fork in ptrace (stopped) state. */
+ fp = find_fork_pid (parent_pid);
+ if (!fp)
+ fp = add_fork (parent_pid);
+ fork_save_infrun_state (fp, 0);
+ }
else
- target_detach (NULL, 0);
+ {
+ target_detach (NULL, 0);
+ }
inferior_ptid = pid_to_ptid (child_pid);
- push_target (&deprecated_child_ops);
+
+ /* Reinstall ourselves, since we might have been removed in
+ target_detach (which does other necessary cleanup). */
+
+ push_target (ops);
/* Reset breakpoints in the child as appropriate. */
follow_inferior_reset_breakpoints ();
{
/* The new child has a pending SIGSTOP. We can't affect it until it
hits the SIGSTOP, but we're already attached. */
- do {
- ret = waitpid (new_pid, &status,
- (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
- } while (ret == -1 && errno == EINTR);
+ ret = my_waitpid (new_pid, &status,
+ (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
if (ret == -1)
perror_with_name (_("waiting for new child"));
else if (ret != new_pid)
if (pid == 0)
return;
- /* If we're stopped while forking and we haven't followed yet, kill the
- other task. We need to do this first because the parent will be
- sleeping if this is a vfork. */
-
- get_last_target_status (&last_ptid, &last);
-
- if (last.kind == TARGET_WAITKIND_FORKED
- || last.kind == TARGET_WAITKIND_VFORKED)
+ /* First cut -- let's crudely do everything inline. */
+ if (forks_exist_p ())
{
- ptrace (PT_KILL, last.value.related_pid, 0, 0);
- wait (&status);
+ linux_fork_killall ();
+ pop_target ();
+ generic_mourn_inferior ();
}
+ else
+ {
+ /* If we're stopped while forking and we haven't followed yet,
+ kill the other task. We need to do this first because the
+ parent will be sleeping if this is a vfork. */
- /* Kill the current process. */
- ptrace (PT_KILL, pid, 0, 0);
- ret = wait (&status);
+ get_last_target_status (&last_ptid, &last);
- /* We might get a SIGCHLD instead of an exit status. This is
- aggravated by the first kill above - a child has just died. */
+ if (last.kind == TARGET_WAITKIND_FORKED
+ || last.kind == TARGET_WAITKIND_VFORKED)
+ {
+ ptrace (PT_KILL, last.value.related_pid, 0, 0);
+ wait (&status);
+ }
- while (ret == pid && WIFSTOPPED (status))
- {
+ /* Kill the current process. */
ptrace (PT_KILL, pid, 0, 0);
ret = wait (&status);
- }
- target_mourn_inferior ();
+ /* We might get a SIGCHLD instead of an exit status. This is
+ aggravated by the first kill above - a child has just died. */
+
+ while (ret == pid && WIFSTOPPED (status))
+ {
+ ptrace (PT_KILL, pid, 0, 0);
+ ret = wait (&status);
+ }
+ target_mourn_inferior ();
+ }
}
/* On GNU/Linux there are no real LWP's. The closest thing to LWP's
return NULL;
}
+/* Record a PTID for later deletion. */
+
+struct saved_ptids
+{
+ ptid_t ptid;
+ struct saved_ptids *next;
+};
+static struct saved_ptids *threads_to_delete;
+
+static void
+record_dead_thread (ptid_t ptid)
+{
+ struct saved_ptids *p = xmalloc (sizeof (struct saved_ptids));
+ p->ptid = ptid;
+ p->next = threads_to_delete;
+ threads_to_delete = p;
+}
+
+/* Delete any dead threads which are not the current thread. */
+
+static void
+prune_lwps (void)
+{
+ struct saved_ptids **p = &threads_to_delete;
+
+ while (*p)
+ if (! ptid_equal ((*p)->ptid, inferior_ptid))
+ {
+ struct saved_ptids *tmp = *p;
+ delete_thread (tmp->ptid);
+ *p = tmp->next;
+ xfree (tmp);
+ }
+ else
+ p = &(*p)->next;
+}
+
+/* Callback for iterate_over_threads that finds a thread corresponding
+ to the given LWP. */
+
+static int
+find_thread_from_lwp (struct thread_info *thr, void *dummy)
+{
+ ptid_t *ptid_p = dummy;
+
+ if (GET_LWP (thr->ptid) && GET_LWP (thr->ptid) == GET_LWP (*ptid_p))
+ return 1;
+ else
+ return 0;
+}
+
+/* Handle the exit of a single thread LP. */
+
+static void
+exit_lwp (struct lwp_info *lp)
+{
+ if (in_thread_list (lp->ptid))
+ {
+ /* Core GDB cannot deal with us deleting the current thread. */
+ if (!ptid_equal (lp->ptid, inferior_ptid))
+ delete_thread (lp->ptid);
+ else
+ record_dead_thread (lp->ptid);
+ printf_unfiltered (_("[%s exited]\n"),
+ target_pid_to_str (lp->ptid));
+ }
+ else
+ {
+ /* Even if LP->PTID is not in the global GDB thread list, the
+ LWP may be - with an additional thread ID. We don't need
+ to print anything in this case; thread_db is in use and
+ already took care of that. But it didn't delete the thread
+ in order to handle zombies correctly. */
+
+ struct thread_info *thr;
+
+ thr = iterate_over_threads (find_thread_from_lwp, &lp->ptid);
+ if (thr && !ptid_equal (thr->ptid, inferior_ptid))
+ delete_thread (thr->ptid);
+ else
+ record_dead_thread (thr->ptid);
+ }
+
+ delete_lwp (lp->ptid);
+}
+
/* Attach to the LWP specified by PID. If VERBOSE is non-zero, print
a message telling the user that a new LWP has been added to the
process. */
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
target_pid_to_str (ptid));
- pid = waitpid (GET_LWP (ptid), &status, 0);
+ pid = my_waitpid (GET_LWP (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
/* Try again with __WCLONE to check cloned processes. */
- pid = waitpid (GET_LWP (ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
lp->cloned = 1;
}
/* FIXME: We should probably accept a list of process id's, and
attach all of them. */
- deprecated_child_ops.to_attach (args, from_tty);
+ linux_ops->to_attach (args, from_tty);
/* Add the initial process as the first LWP to the list. */
lp = add_lwp (BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid)));
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
- pid = waitpid (GET_PID (inferior_ptid), &status, 0);
+ pid = my_waitpid (GET_PID (inferior_ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
/* Try again with __WCLONE to check cloned processes. */
- pid = waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
lp->cloned = 1;
}
sigemptyset (&blocked_mask);
inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid));
- deprecated_child_ops.to_detach (args, from_tty);
+ linux_ops->to_detach (args, from_tty);
}
/* Resume LP. */
{
struct thread_info *tp;
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), 0, TARGET_SIGNAL_0);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ 0, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"RC: PTRACE_CONT %s, 0, 0 (resume sibling)\n",
struct lwp_info *lp;
int resume_all;
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Preparing to %s %s, %s, inferior_ptid %s\n",
+ step ? "step" : "resume",
+ target_pid_to_str (ptid),
+ signo ? strsignal (signo) : "0",
+ target_pid_to_str (inferior_ptid));
+
+ prune_lwps ();
+
/* A specific PTID means `step only this process id'. */
resume_all = (PIDGET (ptid) == -1);
lp->resumed = 1;
/* If we have a pending wait status for this thread, there is no
- point in resuming the process. */
+ point in resuming the process. But first make sure that
+ linux_nat_wait won't preemptively handle the event - we
+ should never take this short-circuit if we are going to
+ leave LP running, since we have skipped resuming all the
+ other threads. This bit of code needs to be synchronized
+ with linux_nat_wait. */
+
+ if (lp->status && WIFSTOPPED (lp->status))
+ {
+ int saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
+
+ if (signal_stop_state (saved_signo) == 0
+ && signal_print_state (saved_signo) == 0
+ && signal_pass_state (saved_signo) == 1)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Not short circuiting for ignored "
+ "status 0x%x\n", lp->status);
+
+ /* FIXME: What should we do if we are supposed to continue
+ this thread with a signal? */
+ gdb_assert (signo == TARGET_SIGNAL_0);
+ signo = saved_signo;
+ lp->status = 0;
+ }
+ }
+
if (lp->status)
{
/* FIXME: What should we do if we are supposed to continue
this thread with a signal? */
gdb_assert (signo == TARGET_SIGNAL_0);
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Short circuiting for status 0x%x\n",
+ lp->status);
+
return;
}
if (resume_all)
iterate_over_lwps (resume_callback, NULL);
- child_resume (ptid, step, signo);
+ linux_ops->to_resume (ptid, step, signo);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLR: %s %s, %s (resume event thread)\n",
gdb_assert (!lp->stopped);
gdb_assert (lp->status == 0);
- pid = waitpid (GET_LWP (lp->ptid), &status, 0);
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
- pid = waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
if (pid == -1 && errno == ECHILD)
{
/* The thread has previously exited. We need to delete it
if (thread_dead)
{
- if (in_thread_list (lp->ptid))
- {
- /* Core GDB cannot deal with us deleting the current thread. */
- if (!ptid_equal (lp->ptid, inferior_ptid))
- delete_thread (lp->ptid);
- printf_unfiltered (_("[%s exited]\n"),
- target_pid_to_str (lp->ptid));
- }
-
- delete_lwp (lp->ptid);
+ exit_lwp (lp);
return 0;
}
int random_selector;
struct lwp_info *event_lp;
- /* Record the wait status for the origional LWP. */
+ /* Record the wait status for the original LWP. */
(*orig_lp)->status = *status;
/* Give preference to any LWP that is being single-stepped. */
return lp->resumed;
}
-#ifdef CHILD_WAIT
+/* Local mourn_inferior -- we need to override mourn_inferior
+ so that we can do something clever if one of several forks
+ has exited. */
+
+static void
+child_mourn_inferior (void)
+{
+ int status;
+
+ if (! forks_exist_p ())
+ {
+ /* Normal case, no other forks available. */
+ super_mourn_inferior ();
+ return;
+ }
+ else
+ {
+ /* Multi-fork case. The current inferior_ptid has exited, but
+ there are other viable forks to debug. Delete the exiting
+ one and context-switch to the first available. */
+ linux_fork_mourn_inferior ();
+ }
+}
/* We need to override child_wait to support attaching to cloned
processes, since a normal wait (as done by the default version)
attached process. */
set_sigio_trap ();
- pid = waitpid (GET_PID (ptid), &status, 0);
+ pid = my_waitpid (GET_PID (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
/* Try again with __WCLONE to check cloned processes. */
- pid = waitpid (GET_PID (ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_PID (ptid), &status, __WCLONE);
if (debug_linux_nat)
{
return pid_to_ptid (pid);
}
-#endif
-
/* Stop an active thread, verify it still exists, then resume it. */
static int
/* Resume the thread. It should halt immediately returning the
pending SIGSTOP. */
registers_changed ();
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), lp->step,
- TARGET_SIGNAL_0);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s %s, 0, 0 (expect SIGSTOP)\n",
{
pid_t lwpid;
- lwpid = waitpid (pid, &status, options);
+ lwpid = my_waitpid (pid, &status, options);
if (lwpid > 0)
{
gdb_assert (pid == -1 || lwpid == pid);
/* Check if the thread has exited. */
if ((WIFEXITED (status) || WIFSIGNALED (status)) && num_lwps > 1)
{
- if (in_thread_list (lp->ptid))
- {
- /* Core GDB cannot deal with us deleting the current
- thread. */
- if (!ptid_equal (lp->ptid, inferior_ptid))
- delete_thread (lp->ptid);
- printf_unfiltered (_("[%s exited]\n"),
- target_pid_to_str (lp->ptid));
- }
-
/* If this is the main thread, we must stop all threads and
verify if they are still alive. This is because in the nptl
thread model, there is no signal issued for exiting LWPs
"LLW: %s exited.\n",
target_pid_to_str (lp->ptid));
- delete_lwp (lp->ptid);
+ exit_lwp (lp);
/* If there is at least one more LWP, then the exit signal
was not the end of the debugged application and should be
has stopped. A similar check is made in stop_wait_callback(). */
if (num_lwps > 1 && !linux_nat_thread_alive (lp->ptid))
{
- if (in_thread_list (lp->ptid))
- {
- /* Core GDB cannot deal with us deleting the current
- thread. */
- if (!ptid_equal (lp->ptid, inferior_ptid))
- delete_thread (lp->ptid);
- printf_unfiltered (_("[%s exited]\n"),
- target_pid_to_str (lp->ptid));
- }
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s exited.\n",
target_pid_to_str (lp->ptid));
- delete_lwp (lp->ptid);
+ exit_lwp (lp);
/* Make sure there is at least one thread running. */
gdb_assert (iterate_over_lwps (running_callback, NULL));
lp->signalled = 0;
registers_changed ();
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), lp->step,
- TARGET_SIGNAL_0);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
newly attached threads may cause an unwanted delay in
getting them running. */
registers_changed ();
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), lp->step, signo);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ lp->step, signo);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s %s, %s (preempt 'handle')\n",
{
do
{
- pid = waitpid (GET_LWP (lp->ptid), NULL, __WCLONE);
+ pid = my_waitpid (GET_LWP (lp->ptid), NULL, __WCLONE);
if (pid != (pid_t) -1 && debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
do
{
- pid = waitpid (GET_LWP (lp->ptid), NULL, 0);
+ pid = my_waitpid (GET_LWP (lp->ptid), NULL, 0);
if (pid != (pid_t) -1 && debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
int from_tty)
{
- deprecated_child_ops.to_create_inferior (exec_file, allargs, env, from_tty);
+ linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
}
static void
sigprocmask (SIG_SETMASK, &normal_mask, NULL);
sigemptyset (&blocked_mask);
- deprecated_child_ops.to_mourn_inferior ();
+ linux_ops->to_mourn_inferior ();
}
-static int
-linux_nat_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
- struct mem_attrib *attrib, struct target_ops *target)
+static LONGEST
+linux_nat_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
{
struct cleanup *old_chain = save_inferior_ptid ();
- int xfer;
+ LONGEST xfer;
if (is_lwp (inferior_ptid))
inferior_ptid = pid_to_ptid (GET_LWP (inferior_ptid));
- xfer = linux_proc_xfer_memory (memaddr, myaddr, len, write, attrib, target);
- if (xfer == 0)
- xfer = child_xfer_memory (memaddr, myaddr, len, write, attrib, target);
+ xfer = linux_ops->to_xfer_partial (ops, object, annex, readbuf, writebuf,
+ offset, len);
do_cleanups (old_chain);
return xfer;
return normal_pid_to_str (ptid);
}
+static void
+linux_nat_fetch_registers (int regnum)
+{
+ /* to_fetch_registers will honor the LWP ID, so we can use it directly. */
+ linux_ops->to_fetch_registers (regnum);
+}
+
+static void
+linux_nat_store_registers (int regnum)
+{
+ /* to_store_registers will honor the LWP ID, so we can use it directly. */
+ linux_ops->to_store_registers (regnum);
+}
+
+static void
+linux_nat_child_post_startup_inferior (ptid_t ptid)
+{
+ linux_ops->to_post_startup_inferior (ptid);
+}
+
static void
init_linux_nat_ops (void)
{
linux_nat_ops.to_detach = linux_nat_detach;
linux_nat_ops.to_resume = linux_nat_resume;
linux_nat_ops.to_wait = linux_nat_wait;
- /* fetch_inferior_registers and store_inferior_registers will
- honor the LWP id, so we can use them directly. */
- linux_nat_ops.to_fetch_registers = fetch_inferior_registers;
- linux_nat_ops.to_store_registers = store_inferior_registers;
- linux_nat_ops.deprecated_xfer_memory = linux_nat_xfer_memory;
+ linux_nat_ops.to_fetch_registers = linux_nat_fetch_registers;
+ linux_nat_ops.to_store_registers = linux_nat_store_registers;
+ linux_nat_ops.to_xfer_partial = linux_nat_xfer_partial;
linux_nat_ops.to_kill = linux_nat_kill;
linux_nat_ops.to_create_inferior = linux_nat_create_inferior;
linux_nat_ops.to_mourn_inferior = linux_nat_mourn_inferior;
linux_nat_ops.to_thread_alive = linux_nat_thread_alive;
linux_nat_ops.to_pid_to_str = linux_nat_pid_to_str;
- linux_nat_ops.to_post_startup_inferior = child_post_startup_inferior;
+ linux_nat_ops.to_post_startup_inferior
+ = linux_nat_child_post_startup_inferior;
linux_nat_ops.to_post_attach = child_post_attach;
linux_nat_ops.to_insert_fork_catchpoint = child_insert_fork_catchpoint;
linux_nat_ops.to_insert_vfork_catchpoint = child_insert_vfork_catchpoint;
int ret = fscanf (mapfile, "%llx-%llx %s %llx %s %llx",
addr, endaddr, permissions, offset, device, inode);
- if (ret > 0 && ret != EOF && *inode != 0)
+ filename[0] = '\0';
+ if (ret > 0 && ret != EOF)
{
/* Eat everything up to EOL for the filename. This will prevent
weird filenames (such as one with embedded whitespace) from
only. */
ret += fscanf (mapfile, "%[^\n]\n", filename);
}
- else
- {
- filename[0] = '\0'; /* no filename */
- fscanf (mapfile, "\n");
- }
+
return (ret != 0 && ret != EOF);
}
char psargs[80] = { '\0' };
char *note_data = NULL;
ptid_t current_ptid = inferior_ptid;
- char *auxv;
+ gdb_byte *auxv;
int auxv_len;
if (get_exec_file (0))
}
}
-int
-linux_proc_xfer_memory (CORE_ADDR addr, char *myaddr, int len, int write,
- struct mem_attrib *attrib, struct target_ops *target)
+/* Implement the to_xfer_partial interface for memory reads using the /proc
+ filesystem. Because we can use a single read() call for /proc, this
+ can be much more efficient than banging away at PTRACE_PEEKTEXT,
+ but it doesn't support writes. */
+
+static LONGEST
+linux_proc_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
{
- int fd, ret;
+ LONGEST ret;
+ int fd;
char filename[64];
- if (write)
+ if (object != TARGET_OBJECT_MEMORY || !readbuf)
return 0;
/* Don't bother for one word. */
32-bit platforms (for instance, SPARC debugging a SPARC64
application). */
#ifdef HAVE_PREAD64
- if (pread64 (fd, myaddr, len, addr) != len)
+ if (pread64 (fd, readbuf, len, offset) != len)
#else
- if (lseek (fd, addr, SEEK_SET) == -1 || read (fd, myaddr, len) != len)
+ if (lseek (fd, offset, SEEK_SET) == -1 || read (fd, readbuf, len) != len)
#endif
ret = 0;
else
fclose (procfile);
}
+static LONGEST
+linux_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+ LONGEST xfer;
+
+ if (object == TARGET_OBJECT_AUXV)
+ return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf,
+ offset, len);
+
+ xfer = linux_proc_xfer_partial (ops, object, annex, readbuf, writebuf,
+ offset, len);
+ if (xfer != 0)
+ return xfer;
+
+ return super_xfer_partial (ops, object, annex, readbuf, writebuf,
+ offset, len);
+}
+
+#ifndef FETCH_INFERIOR_REGISTERS
+
+/* Return the address in the core dump or inferior of register
+ REGNO. */
+
+static CORE_ADDR
+linux_register_u_offset (int regno)
+{
+ /* FIXME drow/2005-09-04: The hardcoded use of register_addr should go
+ away. This requires disentangling the various definitions of it
+ (particularly alpha-nat.c's). */
+ return register_addr (regno, 0);
+}
+
+#endif
+
+/* Create a prototype generic Linux target. The client can override
+ it with local methods. */
+
+struct target_ops *
+linux_target (void)
+{
+ struct target_ops *t;
+
+#ifdef FETCH_INFERIOR_REGISTERS
+ t = inf_ptrace_target ();
+#else
+ t = inf_ptrace_trad_target (linux_register_u_offset);
+#endif
+ t->to_wait = child_wait;
+ t->to_kill = kill_inferior;
+ t->to_insert_fork_catchpoint = child_insert_fork_catchpoint;
+ t->to_insert_vfork_catchpoint = child_insert_vfork_catchpoint;
+ t->to_insert_exec_catchpoint = child_insert_exec_catchpoint;
+ t->to_pid_to_exec_file = child_pid_to_exec_file;
+ t->to_post_startup_inferior = linux_child_post_startup_inferior;
+ t->to_post_attach = child_post_attach;
+ t->to_follow_fork = child_follow_fork;
+ t->to_find_memory_regions = linux_nat_find_memory_regions;
+ t->to_make_corefile_notes = linux_nat_make_corefile_notes;
+
+ super_xfer_partial = t->to_xfer_partial;
+ t->to_xfer_partial = linux_xfer_partial;
+
+ super_mourn_inferior = t->to_mourn_inferior;
+ t->to_mourn_inferior = child_mourn_inferior;
+
+ linux_ops = t;
+ return t;
+}
+
void
_initialize_linux_nat (void)
{
struct sigaction action;
extern void thread_db_init (struct target_ops *);
- deprecated_child_ops.to_find_memory_regions = linux_nat_find_memory_regions;
- deprecated_child_ops.to_make_corefile_notes = linux_nat_make_corefile_notes;
-
add_info ("proc", linux_nat_info_proc_cmd, _("\
Show /proc process information about any running process.\n\
Specify any process id, or use the program being debugged by default.\n\
action.sa_handler = sigchld_handler;
sigemptyset (&action.sa_mask);
- action.sa_flags = 0;
+ action.sa_flags = SA_RESTART;
sigaction (SIGCHLD, &action, NULL);
/* Make sure we don't block SIGCHLD during a sigsuspend. */
sigemptyset (&blocked_mask);
- deprecated_add_show_from_set
- (add_set_cmd ("lin-lwp", no_class, var_zinteger,
- (char *) &debug_linux_nat,
- "Set debugging of GNU/Linux lwp module.\n\
-Enables printf debugging output.\n", &setdebuglist), &showdebuglist);
+ add_setshow_zinteger_cmd ("lin-lwp", no_class, &debug_linux_nat, _("\
+Set debugging of GNU/Linux lwp module."), _("\
+Show debugging of GNU/Linux lwp module."), _("\
+Enables printf debugging output."),
+ NULL,
+ show_debug_linux_nat,
+ &setdebuglist, &showdebuglist);
}
\f
if (ms == NULL)
return 0;
- if (target_read_memory (SYMBOL_VALUE_ADDRESS (ms), (char *) &signo,
+ if (target_read_memory (SYMBOL_VALUE_ADDRESS (ms), (gdb_byte *) &signo,
sizeof (signo)) != 0)
return 0;
sigemptyset (set);
restart = get_signo ("__pthread_sig_restart");
+ cancel = get_signo ("__pthread_sig_cancel");
+
+ /* LinuxThreads normally uses the first two RT signals, but in some legacy
+ cases may use SIGUSR1/SIGUSR2. NPTL always uses RT signals, but does
+ not provide any way for the debugger to query the signal numbers -
+ fortunately they don't change! */
+
if (restart == 0)
- return;
+ restart = __SIGRTMIN;
- cancel = get_signo ("__pthread_sig_cancel");
if (cancel == 0)
- return;
+ cancel = __SIGRTMIN + 1;
sigaddset (set, restart);
sigaddset (set, cancel);
action.sa_handler = sigchld_handler;
sigemptyset (&action.sa_mask);
- action.sa_flags = 0;
+ action.sa_flags = SA_RESTART;
sigaction (cancel, &action, NULL);
/* We block the "cancel" signal throughout this code ... */
/* ... except during a sigsuspend. */
sigdelset (&suspend_mask, cancel);
}
+