#include <unistd.h>
#include <sys/syscall.h>
#endif
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
#include "linux-nat.h"
#include "nat/linux-ptrace.h"
#include "nat/linux-procfs.h"
#include "target-descriptions.h"
#include "filestuff.h"
#include "objfiles.h"
+#include "nat/linux-namespaces.h"
+#include "fileio.h"
#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
#define O_LARGEFILE 0
#endif
+/* Does the current host support PTRACE_GETREGSET? */
+enum tribool have_ptrace_getregset = TRIBOOL_UNKNOWN;
+
/* 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;
return 0;
}
+/* Return the ptrace options that we want to try to enable. */
+
+static int
+linux_nat_ptrace_options (int attached)
+{
+ int options = 0;
+
+ if (!attached)
+ options |= PTRACE_O_EXITKILL;
+
+ options |= (PTRACE_O_TRACESYSGOOD
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEEXEC);
+
+ return options;
+}
+
/* Initialize ptrace warnings and check for supported ptrace
features given PID.
static void
linux_init_ptrace (pid_t pid, int attached)
{
- linux_enable_event_reporting (pid, attached);
+ int options = linux_nat_ptrace_options (attached);
+
+ linux_enable_event_reporting (pid, options);
linux_ptrace_init_warnings ();
}
true, the new LWP remains stopped, otherwise it is continued. */
static int
-linux_handle_extended_wait (struct lwp_info *lp, int status,
- int stopping)
+linux_handle_extended_wait (struct lwp_info *lp, int status)
{
int pid = ptid_get_lwp (lp->ptid);
struct target_waitstatus *ourstatus = &lp->waitstatus;
ourstatus->kind = TARGET_WAITKIND_FORKED;
else if (event == PTRACE_EVENT_VFORK)
ourstatus->kind = TARGET_WAITKIND_VFORKED;
- else
+ else if (event == PTRACE_EVENT_CLONE)
{
struct lwp_info *new_lp;
new_lp = add_lwp (ptid_build (ptid_get_pid (lp->ptid), new_pid, 0));
new_lp->cloned = 1;
new_lp->stopped = 1;
-
- if (WSTOPSIG (status) != SIGSTOP)
- {
- /* This can happen if someone starts sending signals to
- the new thread before it gets a chance to run, which
- have a lower number than SIGSTOP (e.g. SIGUSR1).
- This is an unlikely case, and harder to handle for
- fork / vfork than for clone, so we do not try - but
- we handle it for clone events here. We'll send
- the other signal on to the thread below. */
-
- new_lp->signalled = 1;
- }
- else
- {
- struct thread_info *tp;
-
- /* When we stop for an event in some other thread, and
- pull the thread list just as this thread has cloned,
- we'll have seen the new thread in the thread_db list
- before handling the CLONE event (glibc's
- pthread_create adds the new thread to the thread list
- before clone'ing, and has the kernel fill in the
- thread's tid on the clone call with
- CLONE_PARENT_SETTID). If that happened, and the core
- had requested the new thread to stop, we'll have
- killed it with SIGSTOP. But since SIGSTOP is not an
- RT signal, it can only be queued once. We need to be
- careful to not resume the LWP if we wanted it to
- stop. In that case, we'll leave the SIGSTOP pending.
- It will later be reported as GDB_SIGNAL_0. */
- tp = find_thread_ptid (new_lp->ptid);
- if (tp != NULL && tp->stop_requested)
- new_lp->last_resume_kind = resume_stop;
- else
- status = 0;
- }
+ new_lp->resumed = 1;
/* If the thread_db layer is active, let it record the user
level thread id and status, and add the thread to GDB's
}
/* Even if we're stopping the thread for some reason
- internal to this module, from the user/frontend's
- perspective, this new thread is running. */
+ internal to this module, from the perspective of infrun
+ and the user/frontend, this new thread is running until
+ it next reports a stop. */
set_running (new_lp->ptid, 1);
- if (!stopping)
- {
- set_executing (new_lp->ptid, 1);
- /* thread_db_attach_lwp -> lin_lwp_attach_lwp forced
- resume_stop. */
- new_lp->last_resume_kind = resume_continue;
- }
+ set_executing (new_lp->ptid, 1);
- if (status != 0)
+ if (WSTOPSIG (status) != SIGSTOP)
{
+ /* This can happen if someone starts sending signals to
+ the new thread before it gets a chance to run, which
+ have a lower number than SIGSTOP (e.g. SIGUSR1).
+ This is an unlikely case, and harder to handle for
+ fork / vfork than for clone, so we do not try - but
+ we handle it for clone events here. */
+
+ new_lp->signalled = 1;
+
/* We created NEW_LP so it cannot yet contain STATUS. */
gdb_assert (new_lp->status == 0);
new_lp->status = status;
}
- new_lp->resumed = !stopping;
return 1;
}
/* Check if the thread has exited. */
if (WIFEXITED (status) || WIFSIGNALED (status))
{
+ if (ptid_get_pid (lp->ptid) == ptid_get_lwp (lp->ptid))
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog, "WL: Process %d exited.\n",
+ ptid_get_pid (lp->ptid));
+
+ /* This is the leader exiting, it means the whole
+ process is gone. Store the status to report to the
+ core. Store it in lp->waitstatus, because lp->status
+ would be ambiguous (W_EXITCODE(0,0) == 0). */
+ store_waitstatus (&lp->waitstatus, status);
+ return 0;
+ }
+
thread_dead = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "WL: %s exited.\n",
if (lp->must_set_ptrace_flags)
{
struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid));
+ int options = linux_nat_ptrace_options (inf->attach_flag);
- linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag);
+ linux_enable_event_reporting (ptid_get_lwp (lp->ptid), options);
lp->must_set_ptrace_flags = 0;
}
fprintf_unfiltered (gdb_stdlog,
"WL: Handling extended status 0x%06x\n",
status);
- linux_handle_extended_wait (lp, status, 1);
+ linux_handle_extended_wait (lp, status);
return 0;
}
if (WIFSTOPPED (status) && lp->must_set_ptrace_flags)
{
struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid));
+ int options = linux_nat_ptrace_options (inf->attach_flag);
- linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag);
+ linux_enable_event_reporting (ptid_get_lwp (lp->ptid), options);
lp->must_set_ptrace_flags = 0;
}
fprintf_unfiltered (gdb_stdlog,
"LLW: Handling extended status 0x%06x\n",
status);
- if (linux_handle_extended_wait (lp, status, 0))
+ if (linux_handle_extended_wait (lp, status))
return NULL;
}
}
}
- gdb_assert (lp->resumed);
-
+ /* Note that even if the leader was ptrace-stopped, it can still
+ exit, if e.g., some other thread brings down the whole
+ process (calls `exit'). So don't assert that the lwp is
+ resumed. */
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "Process %ld exited\n",
- ptid_get_lwp (lp->ptid));
+ "Process %ld exited (resumed=%d)\n",
+ ptid_get_lwp (lp->ptid), lp->resumed);
/* This was the last lwp in the process. Since events are
serialized to GDB core, we may not be able report this one
{
ptid_t *wait_ptid_p = data;
- if (lp->stopped
- && lp->resumed
- && !lwp_status_pending_p (lp))
+ if (!lp->stopped)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RSRL: NOT resuming LWP %s, not stopped\n",
+ target_pid_to_str (lp->ptid));
+ }
+ else if (!lp->resumed)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RSRL: NOT resuming LWP %s, not resumed\n",
+ target_pid_to_str (lp->ptid));
+ }
+ else if (lwp_status_pending_p (lp))
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RSRL: NOT resuming LWP %s, has pending status\n",
+ target_pid_to_str (lp->ptid));
+ }
+ else
{
struct regcache *regcache = get_thread_regcache (lp->ptid);
struct gdbarch *gdbarch = get_regcache_arch (regcache);
static char *
linux_child_pid_to_exec_file (struct target_ops *self, int pid)
{
- static char buf[PATH_MAX];
- char name[PATH_MAX];
-
- xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
- memset (buf, 0, PATH_MAX);
- if (readlink (name, buf, PATH_MAX - 1) <= 0)
- strcpy (buf, name);
-
- return buf;
+ return linux_proc_pid_to_exec_file (pid);
}
/* Implement the to_xfer_partial interface for memory reads using the /proc
return -1;
}
+/* Implementation of to_filesystem_is_local. */
+
+static int
+linux_nat_filesystem_is_local (struct target_ops *ops)
+{
+ struct inferior *inf = current_inferior ();
+
+ if (inf->fake_pid_p || inf->pid == 0)
+ return 1;
+
+ return linux_ns_same (inf->pid, LINUX_NS_MNT);
+}
+
+/* Convert the INF argument passed to a to_fileio_* method
+ to a process ID suitable for passing to its corresponding
+ linux_mntns_* function. If INF is non-NULL then the
+ caller is requesting the filesystem seen by INF. If INF
+ is NULL then the caller is requesting the filesystem seen
+ by the GDB. We fall back to GDB's filesystem in the case
+ that INF is non-NULL but its PID is unknown. */
+
+static pid_t
+linux_nat_fileio_pid_of (struct inferior *inf)
+{
+ if (inf == NULL || inf->fake_pid_p || inf->pid == 0)
+ return getpid ();
+ else
+ return inf->pid;
+}
+
+/* Implementation of to_fileio_open. */
+
+static int
+linux_nat_fileio_open (struct target_ops *self,
+ struct inferior *inf, const char *filename,
+ int flags, int mode, int *target_errno)
+{
+ int nat_flags;
+ mode_t nat_mode;
+ int fd;
+
+ if (fileio_to_host_openflags (flags, &nat_flags) == -1
+ || fileio_to_host_mode (mode, &nat_mode) == -1)
+ {
+ *target_errno = FILEIO_EINVAL;
+ return -1;
+ }
+
+ fd = linux_mntns_open_cloexec (linux_nat_fileio_pid_of (inf),
+ filename, nat_flags, nat_mode);
+ if (fd == -1)
+ *target_errno = host_to_fileio_error (errno);
+
+ return fd;
+}
+
+/* Implementation of to_fileio_readlink. */
+
+static char *
+linux_nat_fileio_readlink (struct target_ops *self,
+ struct inferior *inf, const char *filename,
+ int *target_errno)
+{
+ char buf[PATH_MAX];
+ int len;
+ char *ret;
+
+ len = linux_mntns_readlink (linux_nat_fileio_pid_of (inf),
+ filename, buf, sizeof (buf));
+ if (len < 0)
+ {
+ *target_errno = host_to_fileio_error (errno);
+ return NULL;
+ }
+
+ ret = xmalloc (len + 1);
+ memcpy (ret, buf, len);
+ ret[len] = '\0';
+ return ret;
+}
+
+/* Implementation of to_fileio_unlink. */
+
+static int
+linux_nat_fileio_unlink (struct target_ops *self,
+ struct inferior *inf, const char *filename,
+ int *target_errno)
+{
+ int ret;
+
+ ret = linux_mntns_unlink (linux_nat_fileio_pid_of (inf),
+ filename);
+ if (ret == -1)
+ *target_errno = host_to_fileio_error (errno);
+
+ return ret;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
t->to_core_of_thread = linux_nat_core_of_thread;
+ t->to_filesystem_is_local = linux_nat_filesystem_is_local;
+ t->to_fileio_open = linux_nat_fileio_open;
+ t->to_fileio_readlink = linux_nat_fileio_readlink;
+ t->to_fileio_unlink = linux_nat_fileio_unlink;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
show_debug_linux_nat,
&setdebuglist, &showdebuglist);
+ add_setshow_boolean_cmd ("linux-namespaces", class_maintenance,
+ &debug_linux_namespaces, _("\
+Set debugging of GNU/Linux namespaces module."), _("\
+Show debugging of GNU/Linux namespaces module."), _("\
+Enables printf debugging output."),
+ NULL,
+ NULL,
+ &setdebuglist, &showdebuglist);
+
/* Save this mask as the default. */
sigprocmask (SIG_SETMASK, NULL, &normal_mask);
sigdelset (&suspend_mask, SIGCHLD);
sigemptyset (&blocked_mask);
-
- /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
- support read-only process state. */
- linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
- | PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEEXEC);
}
\f