/* GNU/Linux native-dependent code common to multiple platforms.
- Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+
+ Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbthread.h"
#include "gdbcmd.h"
#include "regcache.h"
+#include <sys/param.h> /* for MAXPATHLEN */
+#include <sys/procfs.h> /* for elf_gregset etc. */
+#include "elf-bfd.h" /* for elfcore_write_* */
+#include "gregset.h" /* for gregset */
+#include "gdbcore.h" /* for get_exec_file */
+#include <ctype.h> /* for isdigit */
+#include "gdbthread.h" /* for struct thread_info etc. */
+#include "gdb_stat.h" /* for struct stat */
+#include <fcntl.h> /* for O_RDONLY */
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
/* If the system headers did not provide the constants, hard-code the normal
values. */
#define PTRACE_EVENT_VFORK 2
#define PTRACE_EVENT_CLONE 3
#define PTRACE_EVENT_EXEC 4
-#define PTRACE_EVENT_VFORKDONE 5
+#define PTRACE_EVENT_VFORK_DONE 5
#define PTRACE_EVENT_EXIT 6
#endif /* PTRACE_EVENT_FORK */
#endif
static int debug_linux_nat;
-
-extern struct target_ops child_ops;
+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;
ptrace (PTRACE_TRACEME, 0, 0, 0);
kill (getpid (), SIGSTOP);
fork ();
- exit (0);
+ _exit (0);
+}
+
+/* Wrapper function for waitpid which handles EINTR. */
+
+static int
+my_waitpid (int pid, int *status, int flags)
+{
+ int ret;
+ do
+ {
+ ret = waitpid (pid, status, flags);
+ }
+ while (ret == -1 && errno == EINTR);
+
+ return ret;
}
-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. We
+/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.
+
+ First, we try to enable fork tracing on ORIGINAL_PID. If this fails,
+ we know that the feature is not available. This may change the tracing
+ options for ORIGINAL_PID, but we'll be setting them shortly anyway.
+
+ However, if it succeeds, we don't know for sure that the feature is
+ available; old versions of PTRACE_SETOPTIONS ignored unknown options. We
create a child process, attach to it, use PTRACE_SETOPTIONS to enable
- fork tracing, and let it fork. If the process exits, we assume that
- we can't use TRACEFORK; if we get the fork notification, and we can
- extract the new child's PID, then we assume that we can. */
+ fork tracing, and let it fork. If the process exits, we assume that we
+ can't use TRACEFORK; if we get the fork notification, and we can extract
+ the new child's PID, then we assume that we can. */
static void
-linux_test_for_tracefork (void)
+linux_test_for_tracefork (int original_pid)
{
int child_pid, ret, status;
long second_pid;
+ linux_supports_tracefork_flag = 0;
+ linux_supports_tracevforkdone_flag = 0;
+
+ ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK);
+ if (ret != 0)
+ return;
+
child_pid = fork ();
if (child_pid == -1)
- perror_with_name ("linux_test_for_tracefork: fork");
+ perror_with_name (("fork"));
if (child_pid == 0)
linux_tracefork_child ();
- ret = waitpid (child_pid, &status, 0);
+ ret = my_waitpid (child_pid, &status, 0);
if (ret == -1)
- perror_with_name ("linux_test_for_tracefork: waitpid");
+ perror_with_name (("waitpid"));
else if (ret != child_pid)
- error ("linux_test_for_tracefork: waitpid: unexpected result %d.", ret);
+ error (_("linux_test_for_tracefork: waitpid: unexpected result %d."), ret);
if (! WIFSTOPPED (status))
- error ("linux_test_for_tracefork: waitpid: unexpected status %d.", status);
-
- linux_supports_tracefork_flag = 0;
+ error (_("linux_test_for_tracefork: waitpid: unexpected status %d."), status);
ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
if (ret != 0)
{
- ptrace (PTRACE_KILL, child_pid, 0, 0);
- waitpid (child_pid, &status, 0);
+ ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+ if (ret != 0)
+ {
+ warning (_("linux_test_for_tracefork: failed to kill child"));
+ return;
+ }
+
+ ret = my_waitpid (child_pid, &status, 0);
+ if (ret != child_pid)
+ warning (_("linux_test_for_tracefork: failed to wait for killed child"));
+ else if (!WIFSIGNALED (status))
+ warning (_("linux_test_for_tracefork: unexpected wait status 0x%x from "
+ "killed child"), status);
+
return;
}
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
linux_supports_tracevforkdone_flag = (ret == 0);
- ptrace (PTRACE_CONT, child_pid, 0, 0);
- ret = waitpid (child_pid, &status, 0);
+ ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
+ if (ret != 0)
+ warning (_("linux_test_for_tracefork: failed to resume child"));
+
+ ret = my_waitpid (child_pid, &status, 0);
+
if (ret == child_pid && WIFSTOPPED (status)
&& status >> 16 == PTRACE_EVENT_FORK)
{
int second_status;
linux_supports_tracefork_flag = 1;
- waitpid (second_pid, &second_status, 0);
- ptrace (PTRACE_DETACH, second_pid, 0, 0);
+ my_waitpid (second_pid, &second_status, 0);
+ ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
+ if (ret != 0)
+ warning (_("linux_test_for_tracefork: failed to kill second child"));
}
}
+ else
+ warning (_("linux_test_for_tracefork: unexpected result from waitpid "
+ "(%d, status 0x%x)"), ret, status);
- if (WIFSTOPPED (status))
- {
- ptrace (PTRACE_DETACH, child_pid, 0, 0);
- waitpid (child_pid, &status, 0);
- }
+ ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+ if (ret != 0)
+ warning (_("linux_test_for_tracefork: failed to kill child"));
+ my_waitpid (child_pid, &status, 0);
}
/* Return non-zero iff we have tracefork functionality available.
This function also sets linux_supports_tracefork_flag. */
static int
-linux_supports_tracefork (void)
+linux_supports_tracefork (int pid)
{
if (linux_supports_tracefork_flag == -1)
- linux_test_for_tracefork ();
+ linux_test_for_tracefork (pid);
return linux_supports_tracefork_flag;
}
static int
-linux_supports_tracevforkdone (void)
+linux_supports_tracevforkdone (int pid)
{
if (linux_supports_tracefork_flag == -1)
- linux_test_for_tracefork ();
+ linux_test_for_tracefork (pid);
return linux_supports_tracevforkdone_flag;
}
void
linux_enable_event_reporting (ptid_t ptid)
{
- int pid = ptid_get_pid (ptid);
+ int pid = ptid_get_lwp (ptid);
int options;
- if (! linux_supports_tracefork ())
+ if (pid == 0)
+ pid = ptid_get_pid (ptid);
+
+ if (! linux_supports_tracefork (pid))
return;
options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
| PTRACE_O_TRACECLONE;
- if (linux_supports_tracevforkdone ())
+ if (linux_supports_tracevforkdone (pid))
options |= PTRACE_O_TRACEVFORKDONE;
/* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
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);
+ if (debug_linux_nat)
+ {
+ target_terminal_ours ();
+ fprintf_unfiltered (gdb_stdlog,
+ "Detaching after fork from child process %d.\n",
+ child_pid);
+ }
ptrace (PTRACE_DETACH, child_pid, 0, 0);
if (has_vforked)
{
- if (linux_supports_tracevforkdone ())
+ gdb_assert (linux_supports_tracefork_flag >= 0);
+ if (linux_supports_tracevforkdone (0))
{
int status;
ptrace (PTRACE_CONT, parent_pid, 0, 0);
- waitpid (parent_pid, &status, __WALL);
- if ((status >> 16) != PTRACE_EVENT_VFORKDONE)
- warning ("Unexpected waitpid result %06x when waiting for "
- "vfork-done", status);
+ my_waitpid (parent_pid, &status, __WALL);
+ if ((status >> 16) != PTRACE_EVENT_VFORK_DONE)
+ warning (_("Unexpected waitpid result %06x when waiting for "
+ "vfork-done"), status);
}
else
{
/* 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_unfiltered (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
target_detach (NULL, 0);
inferior_ptid = pid_to_ptid (child_pid);
- push_target (&child_ops);
+ push_target (&deprecated_child_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");
+ perror_with_name (_("waiting for new child"));
else if (ret != new_pid)
internal_error (__FILE__, __LINE__,
- "wait returned unexpected PID %d", ret);
+ _("wait returned unexpected PID %d"), ret);
else if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP)
internal_error (__FILE__, __LINE__,
- "wait returned unexpected status 0x%x", status);
+ _("wait returned unexpected status 0x%x"), status);
}
if (event == PTRACE_EVENT_FORK)
}
internal_error (__FILE__, __LINE__,
- "unknown ptrace event %d", event);
+ _("unknown ptrace event %d"), event);
}
\f
-int
+void
child_insert_fork_catchpoint (int pid)
{
- if (! linux_supports_tracefork ())
- error ("Your system does not support fork catchpoints.");
-
- return 0;
+ if (! linux_supports_tracefork (pid))
+ error (_("Your system does not support fork catchpoints."));
}
-int
+void
child_insert_vfork_catchpoint (int pid)
{
- if (!linux_supports_tracefork ())
- error ("Your system does not support vfork catchpoints.");
-
- return 0;
+ if (!linux_supports_tracefork (pid))
+ error (_("Your system does not support vfork catchpoints."));
}
-int
+void
child_insert_exec_catchpoint (int pid)
{
- if (!linux_supports_tracefork ())
- error ("Your system does not support exec catchpoints.");
-
- return 0;
+ if (!linux_supports_tracefork (pid))
+ error (_("Your system does not support exec catchpoints."));
}
void
|| last.kind == TARGET_WAITKIND_VFORKED)
{
ptrace (PT_KILL, last.value.related_pid, 0, 0);
- ptrace_wait (null_ptid, &status);
+ wait (&status);
}
/* Kill the current process. */
ptrace (PT_KILL, pid, 0, 0);
- ret = ptrace_wait (null_ptid, &status);
+ ret = wait (&status);
/* 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 = ptrace_wait (null_ptid, &status);
+ ret = wait (&status);
}
target_mourn_inferior ();
/* This module's target-specific operations. */
static struct target_ops linux_nat_ops;
-/* The standard child operations. */
-extern struct target_ops child_ops;
-
/* Since we cannot wait (in linux_nat_wait) for the initial process and
any cloned processes with a single call to waitpid, we have to use
the WNOHANG flag and call waitpid in a loop. To optimize
}
if (verbose)
- printf_filtered ("[New %s]\n", target_pid_to_str (ptid));
+ printf_filtered (_("[New %s]\n"), target_pid_to_str (ptid));
found_lp = lp = find_lwp_pid (ptid);
if (lp == NULL)
int status;
if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
- error ("Can't attach %s: %s", target_pid_to_str (ptid),
+ error (_("Can't attach %s: %s"), target_pid_to_str (ptid),
safe_strerror (errno));
if (debug_linux_nat)
"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. */
- child_ops.to_attach (args, from_tty);
+ deprecated_child_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));
+ 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;
}
errno = 0;
if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
- error ("Can't continue %s: %s", target_pid_to_str (lp->ptid),
+ error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
if (debug_linux_nat)
errno = 0;
if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
- error ("Can't detach %s: %s", target_pid_to_str (lp->ptid),
+ error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
if (debug_linux_nat)
sigemptyset (&blocked_mask);
inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid));
- child_ops.to_detach (args, from_tty);
+ deprecated_child_ops.to_detach (args, from_tty);
}
/* Resume LP. */
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
/* 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",
+ printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
if (lp->status)
{
if (debug_linux_nat)
- printf_unfiltered ("FC: LP has pending status %06x\n", lp->status);
+ printf_unfiltered (_("FC: LP has pending status %06x\n"), lp->status);
if (WIFSTOPPED (lp->status) && sigismember (flush_mask, WSTOPSIG (lp->status)))
lp->status = 0;
}
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)
{
if (pid == -1)
{
- warning ("Child process unexpectedly missing: %s",
+ warning (_("Child process unexpectedly missing: %s"),
safe_strerror (errno));
/* Claim it exited with unknown signal. */
{
pid_t lwpid;
- lwpid = waitpid (pid, &status, options);
+ lwpid = my_waitpid (pid, &status, options);
if (lwpid > 0)
{
gdb_assert (pid == -1 || lwpid == pid);
}
add_thread (lp->ptid);
- printf_unfiltered ("[New %s]\n",
+ printf_unfiltered (_("[New %s]\n"),
target_pid_to_str (lp->ptid));
}
}
thread. */
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
- printf_unfiltered ("[%s exited]\n",
+ printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
thread. */
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
- printf_unfiltered ("[%s exited]\n",
+ printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
if (debug_linux_nat)
{
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)
{
- child_ops.to_create_inferior (exec_file, allargs, env, from_tty);
+ deprecated_child_ops.to_create_inferior (exec_file, allargs, env, from_tty);
}
static void
sigprocmask (SIG_SETMASK, &normal_mask, NULL);
sigemptyset (&blocked_mask);
- child_ops.to_mourn_inferior ();
+ deprecated_child_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)
+linux_nat_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len,
+ int write, struct mem_attrib *attrib,
+ struct target_ops *target)
{
struct cleanup *old_chain = save_inferior_ptid ();
int xfer;
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.to_xfer_memory = linux_nat_xfer_memory;
+ linux_nat_ops.deprecated_xfer_memory = linux_nat_xfer_memory;
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;
arrival of a SIGCHLD. */
}
+/* Accepts an integer PID; Returns a string representing a file that
+ can be opened to get the symbols for the child process. */
+
+char *
+child_pid_to_exec_file (int pid)
+{
+ char *name1, *name2;
+
+ name1 = xmalloc (MAXPATHLEN);
+ name2 = xmalloc (MAXPATHLEN);
+ make_cleanup (xfree, name1);
+ make_cleanup (xfree, name2);
+ memset (name2, 0, MAXPATHLEN);
+
+ sprintf (name1, "/proc/%d/exe", pid);
+ if (readlink (name1, name2, MAXPATHLEN) > 0)
+ return name2;
+ else
+ return name1;
+}
+
+/* Service function for corefiles and info proc. */
+
+static int
+read_mapping (FILE *mapfile,
+ long long *addr,
+ long long *endaddr,
+ char *permissions,
+ long long *offset,
+ char *device, long long *inode, char *filename)
+{
+ int ret = fscanf (mapfile, "%llx-%llx %s %llx %s %llx",
+ addr, endaddr, permissions, offset, device, inode);
+
+ 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
+ confusing this code. It also makes this code more robust in
+ respect to annotations the kernel may add after the filename.
+
+ Note the filename is used for informational purposes
+ only. */
+ ret += fscanf (mapfile, "%[^\n]\n", filename);
+ }
+
+ return (ret != 0 && ret != EOF);
+}
+
+/* Fills the "to_find_memory_regions" target vector. Lists the memory
+ regions in the inferior for a corefile. */
+
+static int
+linux_nat_find_memory_regions (int (*func) (CORE_ADDR,
+ unsigned long,
+ int, int, int, void *), void *obfd)
+{
+ long long pid = PIDGET (inferior_ptid);
+ char mapsfilename[MAXPATHLEN];
+ FILE *mapsfile;
+ long long addr, endaddr, size, offset, inode;
+ char permissions[8], device[8], filename[MAXPATHLEN];
+ int read, write, exec;
+ int ret;
+
+ /* Compose the filename for the /proc memory map, and open it. */
+ sprintf (mapsfilename, "/proc/%lld/maps", pid);
+ if ((mapsfile = fopen (mapsfilename, "r")) == NULL)
+ error (_("Could not open %s."), mapsfilename);
+
+ if (info_verbose)
+ fprintf_filtered (gdb_stdout,
+ "Reading memory regions from %s\n", mapsfilename);
+
+ /* Now iterate until end-of-file. */
+ while (read_mapping (mapsfile, &addr, &endaddr, &permissions[0],
+ &offset, &device[0], &inode, &filename[0]))
+ {
+ size = endaddr - addr;
+
+ /* Get the segment's permissions. */
+ read = (strchr (permissions, 'r') != 0);
+ write = (strchr (permissions, 'w') != 0);
+ exec = (strchr (permissions, 'x') != 0);
+
+ if (info_verbose)
+ {
+ fprintf_filtered (gdb_stdout,
+ "Save segment, %lld bytes at 0x%s (%c%c%c)",
+ size, paddr_nz (addr),
+ read ? 'r' : ' ',
+ write ? 'w' : ' ', exec ? 'x' : ' ');
+ if (filename && filename[0])
+ fprintf_filtered (gdb_stdout, " for %s", filename);
+ fprintf_filtered (gdb_stdout, "\n");
+ }
+
+ /* Invoke the callback function to create the corefile
+ segment. */
+ func (addr, size, read, write, exec, obfd);
+ }
+ fclose (mapsfile);
+ return 0;
+}
+
+/* Records the thread's register state for the corefile note
+ section. */
+
+static char *
+linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid,
+ char *note_data, int *note_size)
+{
+ gdb_gregset_t gregs;
+ gdb_fpregset_t fpregs;
+#ifdef FILL_FPXREGSET
+ gdb_fpxregset_t fpxregs;
+#endif
+ unsigned long lwp = ptid_get_lwp (ptid);
+
+ fill_gregset (&gregs, -1);
+ note_data = (char *) elfcore_write_prstatus (obfd,
+ note_data,
+ note_size,
+ lwp,
+ stop_signal, &gregs);
+
+ fill_fpregset (&fpregs, -1);
+ note_data = (char *) elfcore_write_prfpreg (obfd,
+ note_data,
+ note_size,
+ &fpregs, sizeof (fpregs));
+#ifdef FILL_FPXREGSET
+ fill_fpxregset (&fpxregs, -1);
+ note_data = (char *) elfcore_write_prxfpreg (obfd,
+ note_data,
+ note_size,
+ &fpxregs, sizeof (fpxregs));
+#endif
+ return note_data;
+}
+
+struct linux_nat_corefile_thread_data
+{
+ bfd *obfd;
+ char *note_data;
+ int *note_size;
+ int num_notes;
+};
+
+/* Called by gdbthread.c once per thread. Records the thread's
+ register state for the corefile note section. */
+
+static int
+linux_nat_corefile_thread_callback (struct lwp_info *ti, void *data)
+{
+ struct linux_nat_corefile_thread_data *args = data;
+ ptid_t saved_ptid = inferior_ptid;
+
+ inferior_ptid = ti->ptid;
+ registers_changed ();
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ args->note_data = linux_nat_do_thread_registers (args->obfd,
+ ti->ptid,
+ args->note_data,
+ args->note_size);
+ args->num_notes++;
+ inferior_ptid = saved_ptid;
+ registers_changed ();
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ return 0;
+}
+
+/* Records the register state for the corefile note section. */
+
+static char *
+linux_nat_do_registers (bfd *obfd, ptid_t ptid,
+ char *note_data, int *note_size)
+{
+ registers_changed ();
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ return linux_nat_do_thread_registers (obfd,
+ ptid_build (ptid_get_pid (inferior_ptid),
+ ptid_get_pid (inferior_ptid),
+ 0),
+ note_data, note_size);
+ return note_data;
+}
+
+/* Fills the "to_make_corefile_note" target vector. Builds the note
+ section for a corefile, and returns it in a malloc buffer. */
+
+static char *
+linux_nat_make_corefile_notes (bfd *obfd, int *note_size)
+{
+ struct linux_nat_corefile_thread_data thread_args;
+ struct cleanup *old_chain;
+ char fname[16] = { '\0' };
+ char psargs[80] = { '\0' };
+ char *note_data = NULL;
+ ptid_t current_ptid = inferior_ptid;
+ gdb_byte *auxv;
+ int auxv_len;
+
+ if (get_exec_file (0))
+ {
+ strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname));
+ strncpy (psargs, get_exec_file (0), sizeof (psargs));
+ if (get_inferior_args ())
+ {
+ strncat (psargs, " ", sizeof (psargs) - strlen (psargs));
+ strncat (psargs, get_inferior_args (),
+ sizeof (psargs) - strlen (psargs));
+ }
+ note_data = (char *) elfcore_write_prpsinfo (obfd,
+ note_data,
+ note_size, fname, psargs);
+ }
+
+ /* Dump information for threads. */
+ thread_args.obfd = obfd;
+ thread_args.note_data = note_data;
+ thread_args.note_size = note_size;
+ thread_args.num_notes = 0;
+ iterate_over_lwps (linux_nat_corefile_thread_callback, &thread_args);
+ if (thread_args.num_notes == 0)
+ {
+ /* iterate_over_threads didn't come up with any threads; just
+ use inferior_ptid. */
+ note_data = linux_nat_do_registers (obfd, inferior_ptid,
+ note_data, note_size);
+ }
+ else
+ {
+ note_data = thread_args.note_data;
+ }
+
+ auxv_len = target_auxv_read (¤t_target, &auxv);
+ if (auxv_len > 0)
+ {
+ note_data = elfcore_write_note (obfd, note_data, note_size,
+ "CORE", NT_AUXV, auxv, auxv_len);
+ xfree (auxv);
+ }
+
+ make_cleanup (xfree, note_data);
+ return note_data;
+}
+
+/* Implement the "info proc" command. */
+
+static void
+linux_nat_info_proc_cmd (char *args, int from_tty)
+{
+ long long pid = PIDGET (inferior_ptid);
+ FILE *procfile;
+ char **argv = NULL;
+ char buffer[MAXPATHLEN];
+ char fname1[MAXPATHLEN], fname2[MAXPATHLEN];
+ int cmdline_f = 1;
+ int cwd_f = 1;
+ int exe_f = 1;
+ int mappings_f = 0;
+ int environ_f = 0;
+ int status_f = 0;
+ int stat_f = 0;
+ int all = 0;
+ struct stat dummy;
+
+ if (args)
+ {
+ /* Break up 'args' into an argv array. */
+ if ((argv = buildargv (args)) == NULL)
+ nomem (0);
+ else
+ make_cleanup_freeargv (argv);
+ }
+ while (argv != NULL && *argv != NULL)
+ {
+ if (isdigit (argv[0][0]))
+ {
+ pid = strtoul (argv[0], NULL, 10);
+ }
+ else if (strncmp (argv[0], "mappings", strlen (argv[0])) == 0)
+ {
+ mappings_f = 1;
+ }
+ else if (strcmp (argv[0], "status") == 0)
+ {
+ status_f = 1;
+ }
+ else if (strcmp (argv[0], "stat") == 0)
+ {
+ stat_f = 1;
+ }
+ else if (strcmp (argv[0], "cmd") == 0)
+ {
+ cmdline_f = 1;
+ }
+ else if (strncmp (argv[0], "exe", strlen (argv[0])) == 0)
+ {
+ exe_f = 1;
+ }
+ else if (strcmp (argv[0], "cwd") == 0)
+ {
+ cwd_f = 1;
+ }
+ else if (strncmp (argv[0], "all", strlen (argv[0])) == 0)
+ {
+ all = 1;
+ }
+ else
+ {
+ /* [...] (future options here) */
+ }
+ argv++;
+ }
+ if (pid == 0)
+ error (_("No current process: you must name one."));
+
+ sprintf (fname1, "/proc/%lld", pid);
+ if (stat (fname1, &dummy) != 0)
+ error (_("No /proc directory: '%s'"), fname1);
+
+ printf_filtered (_("process %lld\n"), pid);
+ if (cmdline_f || all)
+ {
+ sprintf (fname1, "/proc/%lld/cmdline", pid);
+ if ((procfile = fopen (fname1, "r")) > 0)
+ {
+ fgets (buffer, sizeof (buffer), procfile);
+ printf_filtered ("cmdline = '%s'\n", buffer);
+ fclose (procfile);
+ }
+ else
+ warning (_("unable to open /proc file '%s'"), fname1);
+ }
+ if (cwd_f || all)
+ {
+ sprintf (fname1, "/proc/%lld/cwd", pid);
+ memset (fname2, 0, sizeof (fname2));
+ if (readlink (fname1, fname2, sizeof (fname2)) > 0)
+ printf_filtered ("cwd = '%s'\n", fname2);
+ else
+ warning (_("unable to read link '%s'"), fname1);
+ }
+ if (exe_f || all)
+ {
+ sprintf (fname1, "/proc/%lld/exe", pid);
+ memset (fname2, 0, sizeof (fname2));
+ if (readlink (fname1, fname2, sizeof (fname2)) > 0)
+ printf_filtered ("exe = '%s'\n", fname2);
+ else
+ warning (_("unable to read link '%s'"), fname1);
+ }
+ if (mappings_f || all)
+ {
+ sprintf (fname1, "/proc/%lld/maps", pid);
+ if ((procfile = fopen (fname1, "r")) > 0)
+ {
+ long long addr, endaddr, size, offset, inode;
+ char permissions[8], device[8], filename[MAXPATHLEN];
+
+ printf_filtered (_("Mapped address spaces:\n\n"));
+ if (TARGET_ADDR_BIT == 32)
+ {
+ printf_filtered ("\t%10s %10s %10s %10s %7s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", " Offset", "objfile");
+ }
+ else
+ {
+ printf_filtered (" %18s %18s %10s %10s %7s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", " Offset", "objfile");
+ }
+
+ while (read_mapping (procfile, &addr, &endaddr, &permissions[0],
+ &offset, &device[0], &inode, &filename[0]))
+ {
+ size = endaddr - addr;
+
+ /* FIXME: carlton/2003-08-27: Maybe the printf_filtered
+ calls here (and possibly above) should be abstracted
+ out into their own functions? Andrew suggests using
+ a generic local_address_string instead to print out
+ the addresses; that makes sense to me, too. */
+
+ if (TARGET_ADDR_BIT == 32)
+ {
+ printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n",
+ (unsigned long) addr, /* FIXME: pr_addr */
+ (unsigned long) endaddr,
+ (int) size,
+ (unsigned int) offset,
+ filename[0] ? filename : "");
+ }
+ else
+ {
+ printf_filtered (" %#18lx %#18lx %#10x %#10x %7s\n",
+ (unsigned long) addr, /* FIXME: pr_addr */
+ (unsigned long) endaddr,
+ (int) size,
+ (unsigned int) offset,
+ filename[0] ? filename : "");
+ }
+ }
+
+ fclose (procfile);
+ }
+ else
+ warning (_("unable to open /proc file '%s'"), fname1);
+ }
+ if (status_f || all)
+ {
+ sprintf (fname1, "/proc/%lld/status", pid);
+ if ((procfile = fopen (fname1, "r")) > 0)
+ {
+ while (fgets (buffer, sizeof (buffer), procfile) != NULL)
+ puts_filtered (buffer);
+ fclose (procfile);
+ }
+ else
+ warning (_("unable to open /proc file '%s'"), fname1);
+ }
+ if (stat_f || all)
+ {
+ sprintf (fname1, "/proc/%lld/stat", pid);
+ if ((procfile = fopen (fname1, "r")) > 0)
+ {
+ int itmp;
+ char ctmp;
+
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Process: %d\n"), itmp);
+ if (fscanf (procfile, "%s ", &buffer[0]) > 0)
+ printf_filtered (_("Exec file: %s\n"), buffer);
+ if (fscanf (procfile, "%c ", &ctmp) > 0)
+ printf_filtered (_("State: %c\n"), ctmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Parent process: %d\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Process group: %d\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Session id: %d\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("TTY: %d\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("TTY owner process group: %d\n"), itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Flags: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Minor faults (no memory page): %u\n"),
+ (unsigned int) itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Minor faults, children: %u\n"),
+ (unsigned int) itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Major faults (memory page faults): %u\n"),
+ (unsigned int) itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Major faults, children: %u\n"),
+ (unsigned int) itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered ("utime: %d\n", itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered ("stime: %d\n", itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered ("utime, children: %d\n", itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered ("stime, children: %d\n", itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("jiffies remaining in current time slice: %d\n"),
+ itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered ("'nice' value: %d\n", itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("jiffies until next timeout: %u\n"),
+ (unsigned int) itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered ("jiffies until next SIGALRM: %u\n",
+ (unsigned int) itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("start time (jiffies since system boot): %d\n"),
+ itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Virtual memory size: %u\n"),
+ (unsigned int) itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Resident set size: %u\n"), (unsigned int) itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered ("rlim: %u\n", (unsigned int) itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Start of text: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("End of text: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0)
+ printf_filtered (_("Start of stack: 0x%x\n"), itmp);
+#if 0 /* Don't know how architecture-dependent the rest is...
+ Anyway the signal bitmap info is available from "status". */
+ if (fscanf (procfile, "%u ", &itmp) > 0) /* FIXME arch? */
+ printf_filtered (_("Kernel stack pointer: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0) /* FIXME arch? */
+ printf_filtered (_("Kernel instr pointer: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Pending signals bitmap: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Blocked signals bitmap: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Ignored signals bitmap: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%d ", &itmp) > 0)
+ printf_filtered (_("Catched signals bitmap: 0x%x\n"), itmp);
+ if (fscanf (procfile, "%u ", &itmp) > 0) /* FIXME arch? */
+ printf_filtered (_("wchan (system call): 0x%x\n"), itmp);
+#endif
+ fclose (procfile);
+ }
+ else
+ warning (_("unable to open /proc file '%s'"), fname1);
+ }
+}
+
+int
+linux_proc_xfer_memory (CORE_ADDR addr, char *myaddr, int len, int write,
+ struct mem_attrib *attrib, struct target_ops *target)
+{
+ int fd, ret;
+ char filename[64];
+
+ if (write)
+ return 0;
+
+ /* Don't bother for one word. */
+ if (len < 3 * sizeof (long))
+ return 0;
+
+ /* We could keep this file open and cache it - possibly one per
+ thread. That requires some juggling, but is even faster. */
+ sprintf (filename, "/proc/%d/mem", PIDGET (inferior_ptid));
+ fd = open (filename, O_RDONLY | O_LARGEFILE);
+ if (fd == -1)
+ return 0;
+
+ /* If pread64 is available, use it. It's faster if the kernel
+ supports it (only one syscall), and it's 64-bit safe even on
+ 32-bit platforms (for instance, SPARC debugging a SPARC64
+ application). */
+#ifdef HAVE_PREAD64
+ if (pread64 (fd, myaddr, len, addr) != len)
+#else
+ if (lseek (fd, addr, SEEK_SET) == -1 || read (fd, myaddr, len) != len)
+#endif
+ ret = 0;
+ else
+ ret = len;
+
+ close (fd);
+ return ret;
+}
+
+/* Parse LINE as a signal set and add its set bits to SIGS. */
+
+static void
+add_line_to_sigset (const char *line, sigset_t *sigs)
+{
+ int len = strlen (line) - 1;
+ const char *p;
+ int signum;
+
+ if (line[len] != '\n')
+ error (_("Could not parse signal set: %s"), line);
+
+ p = line;
+ signum = len * 4;
+ while (len-- > 0)
+ {
+ int digit;
+
+ if (*p >= '0' && *p <= '9')
+ digit = *p - '0';
+ else if (*p >= 'a' && *p <= 'f')
+ digit = *p - 'a' + 10;
+ else
+ error (_("Could not parse signal set: %s"), line);
+
+ signum -= 4;
+
+ if (digit & 1)
+ sigaddset (sigs, signum + 1);
+ if (digit & 2)
+ sigaddset (sigs, signum + 2);
+ if (digit & 4)
+ sigaddset (sigs, signum + 3);
+ if (digit & 8)
+ sigaddset (sigs, signum + 4);
+
+ p++;
+ }
+}
+
+/* Find process PID's pending signals from /proc/pid/status and set
+ SIGS to match. */
+
+void
+linux_proc_pending_signals (int pid, sigset_t *pending, sigset_t *blocked, sigset_t *ignored)
+{
+ FILE *procfile;
+ char buffer[MAXPATHLEN], fname[MAXPATHLEN];
+ int signum;
+
+ sigemptyset (pending);
+ sigemptyset (blocked);
+ sigemptyset (ignored);
+ sprintf (fname, "/proc/%d/status", pid);
+ procfile = fopen (fname, "r");
+ if (procfile == NULL)
+ error (_("Could not open %s"), fname);
+
+ while (fgets (buffer, MAXPATHLEN, procfile) != NULL)
+ {
+ /* Normal queued signals are on the SigPnd line in the status
+ file. However, 2.6 kernels also have a "shared" pending
+ queue for delivering signals to a thread group, so check for
+ a ShdPnd line also.
+
+ Unfortunately some Red Hat kernels include the shared pending
+ queue but not the ShdPnd status field. */
+
+ if (strncmp (buffer, "SigPnd:\t", 8) == 0)
+ add_line_to_sigset (buffer + 8, pending);
+ else if (strncmp (buffer, "ShdPnd:\t", 8) == 0)
+ add_line_to_sigset (buffer + 8, pending);
+ else if (strncmp (buffer, "SigBlk:\t", 8) == 0)
+ add_line_to_sigset (buffer + 8, blocked);
+ else if (strncmp (buffer, "SigIgn:\t", 8) == 0)
+ add_line_to_sigset (buffer + 8, ignored);
+ }
+
+ fclose (procfile);
+}
+
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\
+Specify any of the following keywords for detailed info:\n\
+ mappings -- list of mapped memory regions.\n\
+ stat -- list a bunch of random process info.\n\
+ status -- list a different bunch of random process info.\n\
+ all -- list all available /proc info."));
+
init_linux_nat_ops ();
add_target (&linux_nat_ops);
thread_db_init (&linux_nat_ops);
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
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 ... */